I'd like to set up nuxt with NGINX as load balancer. Additionally, I'd like to add some caching to the images.
Now I get 404 for my images using this:
location ~ ^/img.+\.(?:ico|gif|jpe?g|webp|png|woff2?|eot|otf|ttf|svg|js|css)$ {
expires 365d;
add_header Pragma public;
add_header Cache-Control "public";
try_files $uri $uri/ #proxy;
}
location #proxy {
expires 365d;
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline';";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Cache-Status $upstream_cache_status;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ignore_headers Cache-Control;
proxy_http_version 1.1;
proxy_read_timeout 1m;
proxy_connect_timeout 1m;
proxy_pass http://1649681_app/$request_uri;
#proxy_cache nuxt-cache;
#proxy_cache_bypass $arg_nocache; # probably better to change this
#proxy_cache_valid 200 302 60m; # set this to your needs
#proxy_cache_valid 404 1m; # set this to your needs
#proxy_cache_lock on;
#proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
#proxy_cache_key $uri$is_args$args;
}
It seems like $request_uri is wrong in the line proxy_pass http://1649681_app/$request_uri - but how can I pass the requested path to the #proxy-location?
No, it isn't completely wrong to specify an URI using variables for the proxy_pass directive inside the named (as well as the regex) locations. However such a thing will have a drawback - if you can't specify your upstream address using IP rather than hostname, you'll need either a resolver defined in your configuration (worse) or an additional upstream block to define your 1649681_app backend (better). More details can be found here (exactly the same is applicable to named locations too).
Being that said, how do you think, what a request URI will be passed to the upstream specified in the proxy_pass directive if you won't specify any explicitly? It will be exactly the URI being processed (and if for some reason you'd need to pass a modified URI instead, you'd need to to modify it via the rewrite rules).
For the given configuration, assuming you don't need to modify the request URI or query arguments, you should simply use the
proxy_pass http://1649681_app;
And, do you understand what the $uri/ parameter does exactly mean? It makes the try_files directive to check if the given URI is a directory to search an index file inside it. I really doubt you need it using that kind of regex pattern. Remove it, it is only an extra (some kind of expensive) system kernel stat call. Use
try_files $uri #proxy;
instead.
Related
I have 3 apps that are located in 3 different sites:
auth (localhost:59500)
manage (localhost:59501)
files (localhost:59502)
Manage and files are accessed from a menu in auth
I can access them individually. However, I am unsure of how to set this up in nginx.
This is what I have currently in my nginx conf:
upstream auth {
server auth.web;
}
upstream manage {
server manage.web;
}
upstream files {
server files.web;
}
server {
listen 80;
server_name localhost;
server_tokens off;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
location /{
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_pass http://auth/;
}
location /auth/{
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_pass http://auth/;
}
location /manage/{
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_pass http://manage/;
}
location /files/{
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_pass http://files/;
}
}
My nginx is configured to run on http://localhost:9190
Currently, when I run http://localhost:9190, I get to auth without any issue and am able to login.
However, if I try to access http://localhost:9190/manage/Home, I am able to load the page but all my css shows the error 404. If any error appears, it doesnt go back to http://localhost:9190/manage/Home. Instead, it kicks me back to http://localhost:9190/Home
EDIT:
I have tried this but my css and js scripts still aren't loading. However, I am able to navigate to the manage app
location ^~ /manage{
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_pass http://manage/manage;
}
location ^~ /manage/{
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_pass http://manage/manage/;
}
EDIT2:
I realized that the error css and js files had a Content-Type of text/html
The files that managed to load somehow had application/javascript or text/css
Not sure how to change that.
I managed to find out the answer. There were two changes that needed to happen.
In my webapp, I needed to set a Pathbase in my Startup
app.UsePathBase("/manage");
Then in Nginx,
I needed to set the name in the location like this:
location /Manage/{
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_pass http://manage_svr/Manage/;
}
Is it possible with nginx to rewrite "&" to "&" if it appears in a URL the client sends to nginx?
I'm currently kinda stuck, as some parts of my application (where I have plenty influence on) are calling s3 download links from my minio backend with "&" and others with "&" argument separation in the URL. I'm not sure why this issue exactly occurs, but my idea is to simply make nginx fix these URL calls internally, as they are practically the same. The problem is that minio (my S3 Server) interprets these wrong and denies access to a given resource as s3 signatures do not match anymore with & in the URL. They always have to be "&" instead of "&" !
To be a bit more specific, how the URL differs in some cases, please see the following example:
String I pass:
https://localhost/test/sprites.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=2PsuWGctH4UQmGvEQYjTDsZ2HqGM%2F20220601%2Fminio%2Fs3%2Faws4_request&X-Amz-Date=20220601T172937Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=6da7dc0137d25730d09bebbd54b0e1f0132d58cba318b3cfe11bcde5af608e05
Browser calls:
https://localhost/test/sprites.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=2PsuWGctH4UQmGvEQYjTDsZ2HqGM%2F20220601%2Fminio%2Fs3%2Faws4_request&X-Amz-Date=20220601T172937Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=6da7dc0137d25730d09bebbd54b0e1f0132d58cba318b3cfe11bcde5af608e05
The location at my NGINX config looks like this at the moment of writing:
location / {
auth_jwt_enabled off;
limit_req zone=s3 burst=100 nodelay;
proxy_buffer_size 256k;
proxy_buffers 4 512k;
proxy_busy_buffers_size 512k;
proxy_cache_convert_head off;
proxy_connect_timeout 180;
proxy_hide_header Set-Cookie;
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_http_version 1.1;
proxy_ignore_headers Set-Cookie;
proxy_pass http://127.0.0.1:7777;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
proxy_set_header Authorization $http_authorization;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Connection keep-alive;
add_header Content-disposition "attachment; filename=$1";
default_type application/octet-stream;
}
My guess here is that the browser converts the & to & for some reason, as the strings I'm passing does not contain & at all ...
Unfortunately nginx does not have an ability of globally replacing some strings. A workaround is possible, based on the rewrite ... last, when being triggered inside a location, starts over the NGX_HTTP_FIND_CONFIG_PHASE to find a new location for the rewritten URI. However there is a limitation of maximum 10 iteration rounds of this process, or nginx will throw an internal HTTP 500 error. Here is the idea:
location / {
if ($args ~ "^(?<prefix>.+)&(?<suffix>.+)$") {
rewrite ^ $uri?$prefix&$suffix? last;
}
...
}
Nevertheless I doubt the problem is on the nginx side, so most likely this won't help.
I could not decide the best name for the question.
Essentially what I want to achieve is to set a custom allowed body size for a specific location on the webserver.
On the other hand, I was able to achieve the necessary result already with duplicate code, so I am really looking for a way how to make the code reusable and to better understand the observed behavior.
The server reverse-proxies all API requests to the backend service.
In global nginx config /etc/nginx/nginx.conf I set the rule for max allowed body size like so client_max_body_size 50k;.
Then, in individual server config /etc/nginx/conf.d/example.com I have the following config (simplified):
server {
listen 80;
listen [::]:80;
server_name api.example.com www.api.example.com
location ~* /file/upload {
client_max_body_size 100M;
# crashes without this line
proxy_pass http://localhost:90;
#proxy_pass http://localhost:90/file/upload; # also works
}
location / {
# does not work
#location ~* /file/upload {
# client_max_body_size 100M;
#}
proxy_pass http://localhost:90;
}
}
I am trying to override the max body size for file upload endpoint. See that there is 1 proxy_pass for location /file/upload and another proxy_pass for location / pointing to the same internal service.
Question 1. If I remove the proxy_pass from the location /file/upload then error is returned by the server. (no status code in chrome debugger). Why is this happening? Shouldn't request be propagated further to location /?
Question 2. Why is it not possible to define the sublocation with body size override inside the / location as in commented section in example above? If I set it like this, then 413 error code is returned, which hints that the client_max_body_size rule is ignored..
Question 3. Finally, is it possible to tell nginx, after the request hits the /file/upload location - to apply all the rules from the / section? I guess one solution to this problem would be to move the common configuration into separate file and then import it in both sections.. I was thinking if there is any solution that does not require creating new files?
Here is the reusable config I am talking about basically:
location / {
#.s. kill cache. use in dev
sendfile off;
# kill cache
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
etag off;
# don't cache it
proxy_no_cache 1;
# even if cached, don't try to use it
proxy_cache_bypass 1;
proxy_pass http://localhost:90;
client_max_body_size 100M;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
This is not the final version. If I had to copy this piece of code to 2 sections this would not be very friendly approach. So, it would be nice to hear some nginx lifehacks on how accomplish what I try to accomplish in a most friendly way and get some explanations for observed behavior.
Answer 1
If I remove the proxy_pass from the location /file/upload then error is returned by the server. (no status code in chrome debugger). Why is this happening?
Every location have a so-called content handler. If you don't specify content handler explicitly via proxy_pass (fastcgi_pass, uwsgi_pass, etc.) directive, nginx will try to serve the request locally.
Shouldn't request be propagated further to location /?
Of course not. What makes you think it should?
Answer 2
Why is it not possible to define the sublocation with body size override inside the / location as in commented section in example above? If I set it like this, then 413 error code is returned, which hints that the client_max_body_size rule is ignored..
I'd rather expect you'll get the same error as in the first case since your nested location does not have an explicitly specified content handler via the proxy_pass directive. However the following config is worth to try:
location / {
# all the common configuration
location /file/upload {
client_max_body_size 100M;
proxy_pass http://localhost:90;
}
proxy_pass http://localhost:90;
}
Answer 3
Finally, is it possible to tell nginx, after the request hits the /file/upload location - to apply all the rules from the / section?
No, unless you use a separate file via include directive in both locations. However you can try to move all the upstream related setup directives one level up to the server context:
server {
...
# all the common configuration
location / {
proxy_pass http://localhost:90;
}
location /file/upload {
client_max_body_size 100M;
proxy_pass http://localhost:90;
}
}
Note that some directives (e.g. add_header, proxy_set_header) are inherited from the previous configuration level if and only if there are no those directives defined on the current level.
Very often dynamic settings for different locations can be achieved using the map block in a following way:
map $uri $max_body_size {
~^/file/upload 100M;
default 50k;
}
server {
location / {
...
client_max_body_size $max_body_size;
...
}
}
Unfortunally not every nginx directive accepts variable as its argument. Usually when nginx documentation doesn't explicitly states that some directive can accept variables, it means it cannot, and the client_max_body_size is exactly that kind of directive, so the above configuration won't work.
I'm having a very hard time understanding exactly when nginx overwrites my add_header directives.
I have the following:
include /etc/nginx/vhost.d/ihc.example.com;
location = /auth {
proxy_pass http://sso.example.com/auth/login;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
auth_request /auth;
auth_request_set $saved_set_cookie $upstream_http_set_cookie;
add_header Set-Cookie $saved_set_cookie; #I don't see this header in the response
proxy_pass http://ihc.example.com;
include /etc/nginx/vhost.d/ihc.example.com_location;
}
The problem I'm having is that the auth_request_set $saved_set_cookie $upstream_http_set_cookie; cookie doesn't appear in the response.
Now if I remove the include /etc/nginx/vhost.d/ihc.example.com_location; line, it will appear, but this file contains a shared CORS fix that I would really, really like to keep:
if ($cors = 'trueGET') {
add_header 'Access-Control-Allow-Origin' $http_origin; #I see this header in the response
[...]
How can I achieve both without having to repeat the CORS headers in every location? And what is causing this to become overwritten? As I read the documentation, location should overwrite server, but in this case, what is causing this new "scope"?
I finally found the root cause of my issue:
Ifs are evil
Apparently, using if statements in location blocks leads to unspecified behaviours. Unfortunately for me, this means rethinking my entire setup, and potentially switching to OpenResty instead.
I have a Nginx server configured as a reverse-proxy cache server to a remote Apache server. At this point, all is running fine. Here's a part of my configuration (I've left some irrelevant parts out):
server {
listen 443 ssl http2;
server_name www.mywebsite.com mywebsite.com;
location / {
proxy_pass https://123.123.123.123;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_ignore_headers X-Accel-Expires Expires Vary;
proxy_redirect off;
proxy_cache_revalidate off;
proxy_next_upstream off;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_bypass $bypass $do_not_cache;
proxy_no_cache $do_not_cache;
proxy_cache_valid any 2880m;
proxy_cache_valid 404 1m;
proxy_cache my_cache;
proxy_cache_min_uses 1;
proxy_cache_lock on;
proxy_http_version 1.1;
}
}
Now what I want to do is to serve files from a specific directory from local files stored on the Nginx server. The rest of the content must still be cached from the source server:
//www.mywebsite.com => Serves cached content from //123.123.123.123
//www.mywebsite.com/local => Serves files stored locally on the Nginx server
Is it possible to include another location in the "server" section of the configuration? I tried something like this but it doesn't work:
location /local/ {
root /home/user/public_html/local;
try_files $uri $uri/ =404;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Max-Age' 1728000;
}
Sorry for my english by the way.
You're on the right way, you should define another location with an alias directive:
location /local/ {
alias /home/user/public_html/local;
}
Now, when you request the http://YOUR_DOMAIN/local/page.html, it will serve /home/user/public_html/local/page.html.
Keep in mind that nginx must have read permission in the specified root directory.
Read this for the subtle difference between root and alias directives.