I am facing some issues with the Caddy proxy directive. My headers are all messed up and some wont appear on any request.
demo.raggaer.pw {
tls email#gmail.com
proxy / http://localhost:8080 {
header_upstream Host {>Host}
header_upstream X-Real-IP {remote}
header_upstream X-Forwarded-For {remote}
header_upstream X-Forwarded-Proto {scheme}
}
}
I would expect to see those headers on the chrome console. I dont. Also all header keys are lowercase, is this something bad?
From the Caddy docs:
header_upstream sets headers to be passed to the backend.
The headers you set with this directive are not visible to Chrome. When Chrome makes the request, it sends its own set of headers to Caddy. With the header_upstream directive, Caddy can add its own headers to the ones from Chrome. The backend server (in your case http://localhost:8080) can see both sets of headers, but Chrome has no idea that Caddy did anything.
Also, you'll probably want to use {host} instead of {>Host}. It's likely a little more reliable that way.
Finally, the reason you're seeing lowercase header keys is because Chrome is communicating with Caddy using HTTP/2, in which all header keys are lowercased. From the HTTP/2 spec:
Header field names MUST be converted to lowercase prior to their
encoding in HTTP/2.
Related
I'd like my Rails 5 API-only app, for now running on http://localhost:3000, to only accept requests from my NodeJS front-end app, for now running on http://localhost:8888.
So I configured /config/initializers/cors.rb like this:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "http://localhost:8888"
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
And I wrote this test:
#/spec/request/cors_request_spec.rb
RSpec.feature "CORS protection", type: :request do
it "should accept a request from a whitelisted domain" do
get "/api/v1/bodies.json", nil, "HTTP_ORIGIN": "http://localhost:8888"
expect(response.status).to eql(200)
end
it "should reject a request from a non-whitelisted domain" do
get "/api/v1/bodies.json", nil, "HTTP_ORIGIN": "https://foreign.domain"
expect(response.status).to eql(406)
end
end
The first test is passing as expected. But the second is failing with a response code of 200. Why?
(I'm not wed to a 406 response code by the way; just one that indicates the request will not be fulfilled.)
CORS configuration won’t prevent the server from accepting requests based on the value of the Origin request header. You can’t do that just through CORS configuration.
When you configure CORS support on a server, all that the server does differently is just to send the Access-Control-Allow-Origin response header and other CORS headers.
Enforcement of CORS restrictions is done only by browsers. It’s not enforced by servers.
CORS works like is: regardless of any CORS config you make on the server side, the server continues accepting requests from all clients and origins it otherwise would; and so all clients from all origins continue getting responses from the server just as they otherwise would.
So even when you see an error in browser devtools that a cross-origin request from your frontend JavaScript code failed, you’ll still be able to see the response in browser devtools.
But just because your browser can see the response doesn’t mean the browser will expose it to your frontend code. Browsers only expose responses for cross-origin requests to frontend code running at a particular origin if the server the request went to opts-in to allowing the request by responding with an Access-Control-Allow-Origin header which OKs that origin.
So for any requests with an Origin request header matching https://foreign.domain, the configuration snippet in the question should cause browsers to emit a message on the client side saying http://localhost:3000/api/v1/bodies.json can’t be loaded because there’s no Access-Control-Allow-Origin response header in the response (because your configuration causes the server to only send that header in responses to your whitelisted origins).
But that’s all you can do through CORS. You can’t prevent the server side from accepting and responding to requests from particular origins just by doing any CORS configuration on the server side. If you want to do that, you need to do it using something other than just CORS.
I need to do SSR on my web app. And since it's an international website, I need to know the domain (hostname) that is requesting the website so I can render in the appropriate language for the user.
Example:
https://www.mydomain.co.uk // WILL RENDER IN ENGLISH
https://www.mydomain.es // WILL RENDER IN SPANISH
Here is the setup
An express server on a Cloud Run docker Node.js container
Firebase Hosting will rewrite all requests to the Cloud Run container
Here is how Firebase rewrites the traffic:
firebase.json
"rewrites": [
{
"source": "**",
"run": {
"serviceId": "my-server-id",
"region": "us-central1"
}
}
]
It's working fine. But I've noticed that all requests reach my Cloud Run server with the following hostname:
req.hostname === "https://my-server-id-on-cloud-run.run.app", which is default Cloud Run url for my server service.
I've noticed that Firebase Hosting adds my actual domain name to the following header:
x-forwarded-host: "https://www.mydomain.co.uk" OR "https://www.mydomain.es
Is this the correct header to read my domain from? I didn't find any documentation on this subject.
If a user hits my website either using my app's Firebase default urls (web.app OR firebaseapp.com) or through a connected custom domain, I can count on the fact that the x-forwarded-host header will always be there, correct?
Could there be a situation where x-forwarded-host would contain more than one URL? Like it happens in the x-forwarded-for with IPs?
References:
https://firebase.google.com/docs/hosting/cloud-run
https://firebase.google.com/docs/hosting/full-config#rewrite-cloud-run-container
PS: I've noticed that if I hit my cloud run server directly from the Cloud Run default url .run.app, the x-forwarded-for header is not present, which makes sense, given the fact that it is a direct request, and not a rewritten one.
According to this doc,
This header could be the best option, given all the pros you share plus:
X-Forwarded-Host header is useful to determine which Host was originally used.
This header is used for debugging, statistics, and generating location-dependent content.
The Syntax is: X-Forwarded-Host: <host>, and the Directive is:
<host>
The domain name of the forwarded server.
This means that you can't have moew than 1 value in that header.
I'm currently working with NGINX and Node.
In my NGINX configuration I'm adding the allow-origin header as follows, before passing it off to an authorization port that contains my Node app:
location /auth {
add_header Access-Control-Allow-Origin *;
proxy_pass http://watchdog:3000;
}
In my Node middleware I'm also setting the headers to accept any origin:
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
next();
}
When I do this, my Chrome rightfully complains that I'm setting the header twice:
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
Clearly setting the header both in NGINX and in Node is redundant.
However, what's fascinating (and frustrating) is that when I remove the Node middleware while leaving in the NGINX header, I get the following Chrome console error:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
So basically the error claims that either 1) the header is set too many times, or 2) the header is not set at all.
The error disappeared when I attempted the third possibility: only set the header in Node, and not in NGINX.
To summarize, here are my findings:
Setting the header in NGINX and Node did not work
Setting the header in NGINX only did not work
Setting the header in Node only worked
That said, I'm still lost as to why this was the solution. Why did setting the header only in NGINX do absolutely nothing? Perhaps because of the way NGINX handles the proxy_pass?
Any theories / explanations would be much appreciated.
I am developing an ajax form update on localhost in express.js for learning express.js
The origin in header is alway set to be null by browser for CORS request (tried: firefox, chrome and safari)
Question:
1. "Origin: null" in request header is not the problem of express.js. Is this correct?
Why they(is it the browsers?) set the Origin to null? I think it should look like this one: "localhost:3000/myproject/test/form.html"
Should I use jquery ($.ajax() or $.ajaxSetup()) to set the origin, before calling ajax on localhost?
How to make the origin in header reflect the real situation?
Here is the screenshot: [...the screenshot is gone now...]
I read the following article about the CORS. The origin in header is null, which is not explained. Please help.
http://www.html5rocks.com/en/tutorials/cors/
"The first thing to note is that a valid CORS request always contains an Origin header. This Origin header is added by the browser, and can not be controlled by the user."
How to allow CORS?
The Origin header is null because you are making the CORS request from a local file (I bet the url starts with file://). In order to get a "normal" origin, you need to host the client file on a web server and access that file using http or https.
Also note that the Origin value is set by the browser and can not be overridden by client JavaScript code.
(P.S. I wrote that HTML Rocks article, I'll make a note to add a section about null Origin)
I made the simple hello world NODEJS Server.
I have a enyo web service running in chrome that is trying to access the NODEJS server at http://localhost:3000
When is calls the onSuccess method, no data is loaded and the consule shows the following error
XMLHttpRequest cannot load http://localhost:3000/. Origin http://localhost:81 is not allowed by Access-Control-Allow-Origin.
I tested the nodejs server in the browser, it worked fine.
I tried to set the --disable-web-security, flag in chrome, it did not work.
Does anybody know how to fix this problem? If NOD.js is running on another server, would it work? This security is so confusing.
Ted
For security reasons, browsers limit the requests that a script may make via XMLHttpRequest.
Your requests will only succeed under the following 2 cases:
The origin of the URI that your script loads is the same as the origin of the page on which the script is executing (localhost:81 and localhost:3000 are the same host but different origins);
or, if your browser supports it, the server of the page being requested includes an Access-Control-Allow-Origin header which explicitly authorizes pages served from the origin in question (or pages served from all origins) to make XMLHttpRequests to it.
Try adding the Accesss-Control-Allow-Origin header to whatever is generating the response in your node code, adding a header in some code that looks like this:
response.writeHead(200, {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin' : 'http://localhost:81'
//allow anything by replacing the above with
//'Access-Control-Allow-Origin' : '*'
});