I'm using Nginx as a web server.
This is my nginx.conf file:
server {
listen 80;
root /usr/share/nginx/html;
index index.html index.htm;
location ^~ /start/ {
add_header 'Cross-Origin-Embedder-Policy' 'require-corp';
add_header 'Cross-Origin-Opener-Policy' 'same-origin';
try_files $uri $uri/ /index.html;
}
location / {
try_files $uri $uri/ /index.html;
}
}
When I open this link on the browser, I don't see the header in the network tab in the response headers section:
https://example.com/start/629852d359d2a400034698a2
Actually add_header directive works properly. Most probably headers does not get added since there are no /usr/share/nginx/html/start/629852d359d2a400034698a2 file or directory on your server, so request gets rewritten to /index.html according to the last try_files directive parameter, which in turn being processed by your location / { ... } (since that new URI does not start with a /start/ prefix), and that location does not set any additional headers.
Generally, if those /start/-prefixed URIs could be either internal application routes or external assets links, this would be possible to solve using the map block to evaluate required headers values:
map $uri $add_policies {
~^/start/ 1;
# default value will be an empty string, unless specified explicitly
}
map $add_policies $embedder_policy {
1 require-corp;
}
map $add_policies $opener_policy {
1 same-origin;
}
server {
...
location / {
add_header Cross-Origin-Embedder-Policy $embedder_policy;
add_header Cross-Origin-Opener-Policy $origin_policy;
try_files $uri $uri/ /index.html;
}
}
This solution is based on the add_header behavior, which is not to add the specified header to the response at all if the provided value is an empty string.
However, if you know for sure the URIs where those headers should be added are an app routes rather than links to physically existing files, you have one more option:
server {
...
location ^~ /start/ {
set $embedder_policy require-corp;
set $origin_policy same-origin;
rewrite ^ /index.html last;
}
location / {
add_header Cross-Origin-Embedder-Policy $embedder_policy;
add_header Cross-Origin-Opener-Policy $origin_policy;
try_files $uri $uri/ /index.html;
}
}
This solution should be somewhat more performant since it does not require (some kind of expensive) PCRE library call to perform the regex matching operation.
Update
Reviewing my answers, I just figured out that the last configuration can be made even more simple using rewrite ... break instead of rewrite ... last:
server {
...
location ^~ /start/ {
add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Opener-Policy same-origin;
rewrite ^ /index.html break;
}
location / {
try_files $uri $uri/ /index.html;
}
}
Related
I am trying to define an nginx server configuration as following, but the exact location is never triggered only the general path is triggered. So, the response header is always Access-Control-Allow-Origin. What I want is when I visit "curl -v localhost:8081" it should return me with XYZ header only, because it is exact match, but instead it returns the general one. What am I doing wrong?
server {
listen 8081;
server_name localhost;
#index index.html;
root /usr/share/nginx/wallet;
location / {
try_files $uri /index.html /example.html =404;
add_header Access-Control-Allow-Origin *;
}
location = / {
add_header XYZ "xyxxxxxxxxxxxxx";
}
}
The exact match location is the first location to process the request, but is not the final location to process the request.
Nginx internally rewrites the URI to /index.html and processed this new request in the general location block.
You can force Nginx to process the entire request within the exact match location by adding a try_files statement.
For example:
location = / {
try_files /index.html =404;
add_header XYZ "xyxxxxxxxxxxxxx";
}
I have a nginx.conf that looks like this:
server {
...
root /var/opt/data/web;
...
location ~* \.(?:eot|woff|woff2|ttf|js)$ {
expires 1M;
}
...
location /one {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
}
location /two {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
}
}
when I curl http://localhost/one/ I get the content of index.html stored in /other. But when I curl .../localhost/one/foo.js the file is not found and I get this in the error.log:
open() "/default/foo.js" failed (2: No such file or directory)
I tried other variants like location ~ (one|two), location /one/ or even location ~ /(one|two) but all of them didn't work.
The complete config consists of a lot more locations, but I guess the cause of my problem is the location where I set up .js resources to expire -1 because this prevents changing the root to what I need.
If this matters: I use nginx 1.15.2. In case you are wondering why I have this strange alternatives directory: the web directory is created by a CMS software while alternatives is git pulled.
nginx chooses a one location to process a request. Your location ~* \.(?:eot|woff|woff2|ttf|js)$ block processes any URI that ends with .js, and its root value is inherited from the outer block as /var/opt/data/web.
Where you have multiple roots, you need to ensure that those location blocks take precedence, by using the ^~ modifier. See this document for details.
For example:
server {
...
root /var/opt/data/web;
...
location ~* \.(?:eot|woff|woff2|ttf|js)$ {
expires 1M;
}
...
location ^~ /one {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
location ~* \.(?:eot|woff|woff2|ttf|js)$ {
expires 1M;
}
}
...
}
If you need your expires rule to apply to the other roots, you will need to repeat the location within that scope, as shown above.
As an alternative, the expires directive can be used in conjunction with a map. See this document for details.
For example:
map $request_uri $expires {
default off;
~*\.(eot|woff|woff2|ttf|js)(\?|$) 1M;
}
server {
...
root /var/opt/data/web;
expires $expires;
...
location ^~ /one {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
}
...
}
I have a general nginx rule to serve jpgs from their URI.
So if the URI is "http://example.com/images/1.jpg" it will serve under the form root of the site/images/1.jpg
I want to try and serve the image from alternative path if not found on the original path. How do I write the second location?
Here is what I got:
location ~* ^.+\.(jpg|jpeg|gif|png|svg|js|css|mp3|ogg|mpe?g|avi|zip|gz|bz2?|rar|swf|ico)$ {
try_files $uri $uri/ #fallback;
access_log off;
expires max;
log_not_found off;
}
How do I write the fallback to look for the files in another location like /home/user/anotherfolder?
You can use a named capture in the regular expression location to save the filename for later. You can cascade named locations, to try different roots:
location ~* ^/images(?<filename>/.+\.jpg)$ {
try_files $uri #other;
}
location #other {
root /path/to/alternate;
try_files $filename #fallback;
}
If there is a suitable common parent directory, you can achieve the same thing in a single location block.
location ~* ^/images(?<filename>/.+\.jpg)$ {
root /path/to/ancestor;
try_files /dir/$uri /other/dir/$filename #fallback;
}
I have a nginx server with the following code added to the sites conf file. The first part is an alias to allow the folder called images to be severed when visiting for example: example.com/images
The second part has been added to allow permalinks in wordpress to work. Problem is each of the code blocks work separately but not together. The offending line of code is:
location ~* \.(jpg|jpeg|png|gif|css|js|ico)$ {
expires max;
log_not_found off;
}
This code stops any files being server from example.com/images and shows a 404 error
location /images {
alias /var/www/clients/client0/web6/images;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
location ~* \.(jpg|jpeg|png|gif|css|js|ico)$ {
expires max;
log_not_found off;
}
Why does the cache line conflict?
Use try_files. This way you can work with settings in another block.
Example url: http://your-site.com/img/lorena_improta.jpg
root /var/www/html/stackoverflow;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~* ^/img/(.*)$ {
try_files $uri /48725060/images/$1;
}
location ~* \.(jpe?g|gif|png) {
expires 1h;
}
What i want to do:
Check if request comes from Facebook
Check if URL is like domain.com/2
If above conditions are true - show content from /api/content/item/$1?social=1
If above conditions are false - show "normal page"
It is a single page app. Before my changes configuration looked like this (and it worked):
location / {
root /home/eshlox/projects/XXX/project/project/assets/dist;
try_files $uri $uri/ /index.html =404;
}
I've tried to use if statements:
location / {
set $social 1;
if ($http_user_agent ~* "facebookexternalhit") {
set $social UA;
}
if ($uri ~* "^/(\d+)$") {
set $social "${social}URL";
}
if ($social = UAURL) {
rewrite ^/(\d+)$ /api/content/item/$1?social=1;
}
root /home/eshlox/projects/XXX/project/project/assets/dist;
try_files $uri $uri/ /index.html =404;
}
With this configuration everything works as i expected only if both conditions are true or false.
If one of conditions is true and the second is false (or vice versa) then nginx always returns status 404.
I have found "IfIsEvil" on nginx site, i've tried to use mapping (can i use mapping in this case?) but still i can't resolve this problem.
Any ideas?
Best regards.
There is good article about common pitfalls in nignx wiki.
First, I've moved root directive to server level. Second, location is the best way to check urls. So I rethink your requirements as
if location consist of digits
and request from facebook
we have to rewrite url, and the result is:
root /home/eshlox/projects/XXX/project/project/assets/dist;
location / {
try_files $uri $uri/ /index.html;
}
location ~ "^/\d+$" {
if ($http_user_agent ~* "facebookexternalhit") {
rewrite (.+) /api/content/item$1?social=1;
}
try_files $uri $uri/ /index.html;
}
Also, there is almost no reason to have =404 after /index.html in try_files directive.