-- Leo's gemini proxy
-- Connecting to nox.im:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini; charset=utf-8
When we're exposing services behind an Nginx reverse proxy[1] and our service is an API powering a web frontend, we need to add Access-Control-Allow-Origin headers to avoid Cross-Origin Resource Sharing (CORS) errors.
CORS headers allow a server to specify domains (and schemes, ports) from which a browser should permit loading resources. This is required for example when loading a frontend client in web3[1] from a domain, while loading an API from a different domain, or even talking to a localhost node while in development.
A preflight request is a small request that is sent by the browser before the actual request in order to check if the CORS protocol is understood and a server is aware of headers.
Debugging CORS with curl:
curl -H "Origin: http://domain-or-ip-i-want-to-test.com/" \ -H "Access-Control-Request-Method: POST" \ -X OPTIONS -v \ http://localhost:8899
We should see the request headers:
* Connected to localhost (::1) port 8899 (#0) > OPTIONS / HTTP/1.1 > Host: localhost:8899 > Origin: http://domain-or-ip-i-want-to-test.com/ > Access-Control-Request-Method: POST > Access-Control-Request-Headers: X-Requested-With
and the relevant response headers, here desired:
< Access-Control-Allow-Origin: * < Access-Control-Allow-Methods: GET, POST, OPTIONS, HEAD < Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept
indicating that we allow all origins, and the requested method and headers are supported.
We can add these headers with Nginx as follows:
server { location / { # ... proxy_set_header Origin http://myservice; proxy_hide_header Access-Control-Allow-Origin; if ($request_method ~* "(GET|POST)") { add_header "Access-Control-Allow-Origin" *; } # preflight if ($request_method = OPTIONS ) { add_header "Access-Control-Allow-Origin" *; add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD"; add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept"; return 204; } } }
And restart Nginx by testing the config and reloading it if it's ok:
nginx -t nginx -s reload
upstream solanaRPC { server 127.0.0.1:8899; } upstream solanaWS { # sticky IP session ip_hash; server 127.0.0.1:8900; } server { listen 8891; server_name _; location / { proxy_pass http://solanaRPC; proxy_set_header Origin http://solanaRPC; proxy_hide_header Access-Control-Allow-Origin; # Simple requests if ($request_method ~* "(GET|POST)") { add_header "Access-Control-Allow-Origin" *; } # Preflighted requests if ($request_method = OPTIONS ) { add_header "Access-Control-Allow-Origin" *; add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD"; add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept"; return 204; } } } server { listen 8901; server_name _; location / { proxy_pass http://solanaWS; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Origin http://solanaWS; proxy_hide_header Access-Control-Allow-Origin; # Simple requests if ($request_method ~* "(GET|POST)") { add_header "Access-Control-Allow-Origin" *; } # Preflighted requests if ($request_method = OPTIONS ) { add_header "Access-Control-Allow-Origin" *; add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD"; add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept"; return 204; } } }
And forward the ports to the defaults on localhost:
ssh -L 8899:10.3.141.135:8891 -L 8900:10.3.141.135:8901 -N -f root@10.3.141.135
Note that if you're connecting through the tunnel to localhost, you may encounter the error:
Access to XMLHttpRequest at 'http://localhost:8899/' from origin 'http://10.3.141.135' has been blocked by CORS policy: The request client is not a secure context and the resource is in more-private address space `local`.
Chrome has implemented CORS-RFC1918, which prevents public network resources from requesting private-network resources. We can disable it at `chrome://flags/#block-insecure-private-network-request` link for Chrome[1].
Chrome set block insecure private network requests to disable[1]
The best way we found to make this work for development while not interfering with security of our normal browsing experience by accident, is to install Chromium[1]. That is, if you're not using it normally.
\open -n ~/Downloads/chrome-mac/Chromium.app --args --disable-web-security
A note for completion, for SSL supported sites we don't usually want to mix content and use a path next to our static assets instead of exposing a service on a port. We can add to the default site a new Nginx location to point to `/api/` (with trailing slash):
location /api/ { proxy_pass http://127.0.0.1:8081/; }
The full config looks then as follows:
server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; error_page 404 =200 /index.html; server_name _; location /api/ { proxy_pass http://127.0.0.1:8081/; } location / { try_files $uri $uri/ =404; } }
-- Response ended
-- Page fetched on Fri May 10 15:30:58 2024