How to combine 2 rewrite rule to load webp file in Nginx? - regex

I am running a WordPress website in the Nginx.
And to load the Webp file, the following Rewight rule is used.
It works fine when there is only one code in it.
location ~ /wp-content/(?<path>.+)\.(?<ext>jpe?g|png|gif)$ {
if ($http_accept !~* "image/webp") {
break;
}
add_header Vary Accept;
expires 365d;
try_files /wp-content/uploads-webpc/$path.$ext.webp $uri =404;
}
location ~ /wp-content/(?<path>.+)\.(?<ext>jpe?g|png|gif)$ {
if ($http_accept !~* "image/webp") {
break;
}
add_header Vary Accept;
expires 365d;
try_files /wp-content/$path.$ext.webp $uri =404;
}
But my webp file is split in two paths.
/wp-content/$path.$ext.webp
/wp-content/uploads-webpc/$path.$ext.webp
If i paste these both code into nginx's site.conf at the same time, it won't work.
I'm wondering how can I combine these two into one.
Thank you.

I'm not sure what were you trying to achieve with that if ($http_accept !~* "image/webp") { break; } part. If you think it is a way to exit the current location and search the next one, you are wrong. The break directive will stop processing of any further directives from the ngx_http_rewrite_module (but you haven't had any more of them, so it simply does nothing) and your configuration will serve the request with the WEBP file no matter if the client browser support it or not. The classic way to serve the WEBP file in case the Accept HTTP header contains the image/webp string is to use the following map block:
map $http_accept $suffix {
~image/webp .webp;
default '';
}
server {
...
location ~ \.(jpe?g|png|gif)$ {
add_header Vary Accept;
try_files $uri$suffix $uri =404;
}
...
}
Depending on what of those two files you want to check first, you should use
try_files /wp-content/$path.$ext$suffix /wp-content/uploads-webpc/$path.$ext$suffix $uri =404
or
try_files /wp-content/uploads-webpc/$path.$ext$suffix /wp-content/$path.$ext$suffix $uri =404
and only one location block from your question.

Related

Nginx try_files not working with domain that appends trailing slash

I have a dockerised Django app where nginx uses proxy_pass to send requests to the Django backend. I am looking to pre-publish certain pages that dont change often so Django doesnt have to deal with them.
I am trying to use try_files to check if that page exists locally and pass to Django if not.
Our URL structure requires that all URLs end with a forward slash and we dont use file type suffixes e.g. a page might be www.example.com/hello/. This means the $uri param in nginx in this instance is /hello/ and when try_files looks at that it is expecting a directory due to the trailing slash. If I have a directory with a list of files how do I get try_files to look at them without re-writing the URL to remove the slash as Django requires it?
My nginx conf is below.
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name example.com;
root /home/testuser/example;
location / {
try_files $uri uri/ #site_root;
}
location #site_root {
proxy_pass http://127.0.0.1:12345;
}
}
If I have a file "hello" at /home/testuser/example/hello and call https://www.example.com/hello/ how can I get it to load the file correctly?
P.S. the permissions on the static content folder and its contents are all 777 (for now to rule out permissions issues)
Cheers in advance!
You can point the URI /hello/ to a local file called hello or hello.html using try_files, but you must first extract the filename using a regular expression location. See this document for details.
The advantage of using .html is that you will not need to provide the Content-Type of the response.
For example, using hello.html:
root /path/to/root;
location / {
try_files $uri uri/ #site_root;
}
location ~ ^(?<filename>/.+)/$ {
try_files $filename.html #site_root;
}
location #site_root {
proxy_pass ...;
}
If you prefer to store the local files without an extension, and they are all text/html, you will need to provide the Content-Type. See this document for details.
For example, using hello:
root /path/to/root;
location / {
try_files $uri uri/ #site_root;
}
location ~ ^(?<filename>/.+)/$ {
default_type text/html;
try_files $filename #site_root;
}
location #site_root {
proxy_pass ...;
}
In my case using NextJS, leaving the final slash causes errors.
So here is the solution I found to make it work nicely:
root /path/to/static/files/directory; # no trailing slash
rewrite ^/(.*)/$ /$1 permanent; # automatically remove the trailing slash
location / {
try_files $uri $uri.html $uri/index.html /404.html;
# try the provided uri
# then try adding .html
# then try uri/index.html (ex: homepage)
# finally serve the 404 page
}

Negative lookbehind in nginx location block

I need to create a virtual sub directory in nginx which will serve files from a different location than the root. This virtual directory also runs PHP, but I've omitted the fastcgi config for brevity.
I have two location blocks, the one for the /virtual directory uses alias, while the location block for / has it's own root directive. This root directive gets inherited by the virtual folder, messing up the paths. Is there any way I can exclude urls in /virtual from inheriting the root directive of the / location block?
Here is an excerpt of my server config:
server {
root /path/to;
location ^~ /virtual/ {
alias /path/to/lions/public;
try_files $uri $uri/ /lions/public/index.php?$query_string;
}
location / {
root /path/to/tigers/public;
try_files $uri $uri/ /index.php?$query_string;
}
}
If I browse to hostname/virtual/whatever, nginx looks for the file at
/path/to/tigers/public/lions/public/whatever
instead of
/path/to/lions/public/whatever
I have tried the following pattern to match all urls at / except for those starting with /virtual, but that didn't match anything.
location ^~ ((?!\/virtual)\/.*)$
I have also tried putting both an alias and a root directive in the location block for /virtual, but nginx wouldn't start.
Is there any other way to have a different root for my alias directory?
The value of the location and the value of the alias should either both end in / or neither end in / - due to the way nginx does the text substitution when calculating the $request_filename.
All elements in the try_files statement are URIs, so your final element should be /virtual/index.php?$query_string and not /lions/public/index.php?$query_string. The latter would begin a new search, which would be processed by the location / block. Hence the strange file path.
Finally, there is an unpredictability when using alias and try_files together. See this bug report. The same functionality can be achieved using a if block. See this caution on the use of if.
For example:
location ^~ /virtual/ {
alias /path/to/lions/public/;
if (!-e $request_filename) { rewrite ^ /virtual/index.php?$query_string; }
}

Nginx location block matching rule

I'm new to nginx server.
I'm gonna deploy the php framework such as codeigniter to the nginx server.
My config file is following.
server {
index index.html index.php index.htm;
# set expiration of assets to MAX for caching
location ~* \.(ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
expires max;
log_not_found off;
}
location / {
# Check if a file exists, or route it to index.php.
try_files $uri $uri/ /index.php;
}
location ~* \.php$ {
fastcgi_pass unix:/var/run/php/php7-fpm.sock;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
My question is following.
1)The request uri is like this "www.domain.com/controllername/functionname/param1/param2/"
How does nginx work with this url?
2)The third location block matches the regular expression ".php$".
Is this true only if the uri has ended with ".php"?
(I think so , but that block's fastcgi_split_path_info has different regular expression.)
Question 1) Yes this should work, because the line
try_files $uri $uri/ /index.php;
is handled one by one for a request. First nginx tries to find the file described by the URI, if there's no match, it checks if it is a directory. If not it calls your index.php file. The original URI is handed over with a lot of other HTTP_REQUEST variables and the code from codeigniter takes care of parsing the url, if you config (codeigniter is correct).
The call convention for codeigniter is "www.domain.com/controllername/public_function/param1/param2/"
So normally you don't give the viewname, but the controller and the function name in your URI.
Question 2) The "location" directive only uses the URI path without any GET parameters. The split_path works differen and so it need a different regexp.

Rewrite all requests to index.php, except /static/ directory

Here is my nginx.conf:
if ($host ~ "^example.com$") {
rewrite . /index.php last;
}
How can I ask nginx to serve files inside /static/ directory without rewriting the url?
Not sure why you test the value of $host. The usual way to differentiate host names is with multiple server blocks. See this document for details.
The try_files directive is used to check for the existence of a static file first, then defer to a default action if it does not exist:
server {
...
root ...;
location / {
try_files $uri $uri/ /index.php;
}
location ~ \.php$ { ... }
}
Which would work for any static files (not just those in the /static directory). See this document for details.
However, if you specifically want the /static directory to be handled differently, use a prefix location with the ^~ modifier:
location ^~ /static { }
See this document for details.

Nginx: How to rewrite all URLs except of images?

I'm new to nginx and I want to migrate my website from Apache to nginx. My site has URLs like:
www.mywebsite.com/category/product/balloon
www.mywebsite.com/category/product/shoe
www.mywebsite.com/information/help
etc.
Since I'm using PHP I need to rewrite all URLs to index.php except if it's an image OR if it's a "fake-request". My nginx.config so far:
#block fake requests
location ~* \.(aspx|jsp|cgi)$ {
return 410;
}
#rewrite all requests if it's not a image
location / {
root html;
index index.php 500.html;
if (!-f $request_filename) {
rewrite ^(.*)$ /index.php?q=$1 last;
break;
}
}
error_page 404 /index.php;
# serve static files directly
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico)$ {
access_log off;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME E:/test2/html/$fastcgi_script_name;
include fastcgi_params;
}
This configuration does not work because:
1. It doesn't block fake request to .php files and I can't add .php to (aspx|jsp|cgi)$
2. It doesn't rewrite the URL if the file exists which is wrong: It should only serve static files directly if it's a defined file-type in(jpg|jpeg|gif|css|png|js|ico)$
How can I solve these problems? I really appreciate every answer, clarification or feedback you can give me.
Thanks
Mike
You need to configure the HttpRewriteModule. This module makes it possible to change URI using regular expressions (PCRE), and to redirect and select configuration depending on variables.
If the directives of this module are given at the server level, then they are carried out before the location of the request is determined. If in that selected location there are further rewrite directives, then they also are carried out. If the URI changed as a result of the execution of directives inside location, then location is again determined for the new URI.