So I'm moving my site away from Apache and onto Nginx, and I'm having trouble with this scenario:
User uploads a photo. This photo is resized, and then copied to S3. If there's suitable room on disk (or the file cannot be transferred to S3), a local version is kept.
I want requests for these images (such as http://www.mysite.com/p/1_1.jpg) to first look in the p/ directory. If no local file exists, I want to proxy the request out to S3 and render the image (but not redirect).
In Apache, I did this like so:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^p/([0-9]+_[0-9]+\.jpg)$ http://my_bucket.s3.amazonaws.com/$1 [P,L]
My attempt to replicate this behavior in Nginx is this:
location /p/ {
if (-e $request_filename) {
break;
}
proxy_pass http://my_bucket.s3.amazonaws.com/;
}
What happens is that every request attempts to hit Amazon S3, even if the file exists on disk (and if it doesn't exist on Amazon, I get errors.) If I remove the proxy_pass line, then requests for files on disk DO work.
Any ideas on how to fix this?
Shouldn't this be an example of using try_files?
location /p/ {
try_files $uri #s3;
}
location #s3{
proxy_pass http://my_bucket.s3.amazonaws.com;
}
Make sure there isn't a following slash on the S3 url
You could improve your s3 proxy config like this. Adapted from https://stackoverflow.com/a/44749584:
location /p/ {
try_files $uri #s3;
}
location #s3 {
set $s3_bucket 'your_bucket.s3.amazonaws.com';
set $url_full '$1';
proxy_http_version 1.1;
proxy_set_header Host $s3_bucket;
proxy_set_header Authorization '';
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_hide_header x-amz-meta-server-side-encryption;
proxy_hide_header x-amz-server-side-encryption;
proxy_hide_header Set-Cookie;
proxy_ignore_headers Set-Cookie;
proxy_intercept_errors on;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;
proxy_pass http://$s3_bucket$url_full;
}
Thanks to keep my coderwall post :) For the caching purpose you can improve it a bit:
http {
proxy_cache_path /tmp/cache levels=1:2 keys_zone=S3_CACHE:10m inactive=24h max_size=500m;
proxy_temp_path /tmp/cache/temp;
server {
location ~* ^/cache/(.*) {
proxy_buffering on;
proxy_hide_header Set-Cookie;
proxy_ignore_headers Set-Cookie;
...
proxy_cache S3_CACHE;
proxy_cache_valid 24h;
proxy_pass http://$s3_bucket/$url_full;
}
}
}
One more recommendation is to extend resolver cache upto 5 min:
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;
break isn't doing quite what you expect nginx will do the last thing you ask of it, which makes sense if you start digging around making modules... but basically protect your proxy_pass with the does-not-exist version
if (-f $request_filename) {
break;
}
if(!-f $request_filename)
proxy_pass http://s3;
}
I ended up solving this by checking to see if the file doesn't exist, and if so, rewriting that request. I then handle the re-written request and do the proxy_pass there, like so:
location /p/ {
if (!-f $request_filename) {
rewrite ^/p/(.*)$ /ps3/$1 last;
break;
}
}
location /ps3/ {
proxy_pass http://my_bucket.s3.amazonaws.com/;
}
Related
I'm a newbie when it comes to nginx and server config.
I can't seem to set Browser Cache expiration. I don't have a clue on what I did wrong. When I test it on Gtmetrix is says that "expiration not specified". I hope you can help me with this.
# Virtual host file starts here
server {
listen 80;
access_log /var/log/nginx/access.upholsterycleanigs.com.aulog;
error_log /var/log/nginx/error.upholsterycleanings.com.au.log;
server_name upholsterycleanings.com.au www.upholsterycleanings.com.au;
root /home/upholsterycleanings/public_html;
location / {
location ~.*\.(3gp|gif|jpg|jpeg|png|ico|wmv|avi|asf|asx|mpg|mpeg|mp4|pls|mp3|mid|wav|swf|flv|html|htm|txt|js|css|exe|zip|tar|rar|gz|tgz|bz2|uha|7z|doc|docx|xls|xlsx|pdf|iso|woff|ttf|svg|eot)$ {
expires 30d; #Comment this out if you are using the apache backend cache-control/expires headers.
try_files \$uri \#backend;
}
error_page 405 = \#backend;
error_page 500 = \#custom;
add_header X-Cache "HIT from Backend";
proxy_pass http://172.31.37.201:8181;
include proxy.inc;
}
location \#backend {
internal;
proxy_pass http://172.31.37.201:8181;
include proxy.inc;
}
location \#custom {
internal;
proxy_pass http://172.31.37.201:8181;
include proxy.inc;
}
location ~ .*\.(php|jsp|cgi|pl|py)?\$ {
proxy_pass http://172.31.37.201:8181;
include proxy.inc;
}
location ~ /\.ht {
deny all;
}
}
# Virtual host file ends here
Thank you!
I'm trying to get my head around caching images for my open source image hosting serivce PictShare.
Pictshare has a smart query system where an uploaded image can be in a "virtual subdirectory" that changes the image. For example this is the link to the uploaded stackoverflow logo: https://www.pictshare.net/6cb55fe938.png
I can resize it to 300 width by adding /300/ to the URL before the image name: https://www.pictshare.net/300/6cb55fe938.png
Since I'm dealing with a lot of traffic lately I want my nginx proxy to be able to cache all images in from all virtual sub folders but it's not working. I've read many articles and many stackoverflow posts but no solution worked for me.
So far this my productive vhost file
proxy_cache_path /etc/nginx/cache/pictshare levels=1:2 keys_zone=pictshare:50m max_size=1000m inactive=30d;
proxy_temp_path /etc/nginx/tmp 1 2;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_ignore_headers "Set-Cookie";
proxy_hide_header "Set-Cookie";
proxy_buffering on;
server {
...
location / {
proxy_pass http://otherserver/pictshare/;
include /etc/nginx/proxy_params;
location ~* \.(?:jpg|jpeg|gif|png|ico)$ {
expires max;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_cache_valid 200 301 302 1y;
proxy_cache pictshare;
proxy_pass http://otherserver/pictshare$request_uri;
}
}
}
The problem is that no files are cached and I see every image request on the proxy destination.
The only way I got it to work was by adding a special location to the host file that has caching explicitly enabled:
location /cached {
proxy_cache_valid 200 1y;
proxy_cache pictshare;
expires 1y;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
include /etc/nginx/proxy_params;
proxy_pass http://otherserver/pictshare/thumbs/;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
}
The obvious problem with this solution is that the images are only cached when the request starts with /cached eg: https://www.pictshare.net/cached/6cb55fe938.png
Adding the caching commands to the root directory is no option for me since I don't want the forms and pages to be cached, just the images
Where is my mistake?
proxy_cache_path /etc/nginx/cache/pictshare levels=1:2 keys_zone=my_cache:50m max_size=3g inactive=180m;
proxy_temp_path /etc/nginx/tmp 1 2;
proxy_cache_key "$scheme$request_method$host$request_uri";
location ~* ^.+\.(jpe?g|gif|png|ico|pdf)$ {
access_log off;
include /etc/nginx/proxy.conf;
proxy_pass http://backend;
proxy_cache pictshare;
proxy_cache_valid any 12h;
add_header X-Proxy-Cache $upstream_cache_status;
root /var/www/public_html/cached; }
location / {
include /etc/nginx/proxy.conf;
proxy_pass http://backend;
root /var/www/public_html;
}
nginx first searches for the most specific prefix location given by literal strings regardless of the listed order. In the configuration above the only prefix location is “/” and since it matches any request it will be used as a last resort. Then nginx checks locations given by regular expression in the order listed in the configuration file. The first matching expression stops the search and nginx will use this location. If no regular expression matches a request, then nginx uses the most specific prefix location found earlier.
http://nginx.org/en/docs/http/request_processing.html
Well, i have my site.conf file like this:
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_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_path /etc/nginx/cache/pag levels=1:2 keys_zone=APP:100m inactive=1m;
proxy_temp_path /etc/nginx/cache/tmp;
add_header X-Cache $upstream_cache_status;
server {
listen 80;
root /etc/nginx/html;
index index.html index.htm;
server_name www.example.com;
error_page 404 /404.html;
location /404.html {
internal;
}
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_cache APP;
proxy_cache_valid 200 1m;
proxy_cache_methods POST;
expires 1m;
}
}
With this configuration, everything (including POST request methods) is cached for 1 min, OK.
What i need? I need that only this pages can be cached:
1) www.example.com
2) www.example.com/index.html
3) www.example.com/test/page.html
4) www.example.com/test/text.txt (this is a file requested by POST thru page.html, and i need it cached also)
5) www.example.com/test/page2.php?var1=val1&var2=val2 (val1 and val2 are dynamics)
My question is: What i have to put in location / to match the 1-5 items? Like this:
location (1-5 items match) {
proxy_pass http://127.0.0.1:8080/;
proxy_cache APP;
proxy_cache_valid 200 1m;
proxy_cache_methods POST;
expires 1m;
}
Other pages (not cached) will be automatically redirected to 127.0.0.1:8080. I know this can be do like this:
location / {
proxy_pass http://127.0.0.1:8080/;
}
NOTE 1: Other PHP pages receive POST|GET request methods, but i don't need it in cache, only aboves.
NOTE: 2 127.0.0.1:8080 is an apache server that runs PHP, so i can request PHP pages.
Since apache runs on the same host, simply serve the html files you do not want cached through nginx. As for the php pages, send the correct expiration headers in your application and everything will work correctly.
I tried to setup at least 2 servers with nginx (origin + edge). both compiled with the mp4-module. The origin holds all my mp4-files. Edge is configured with all the caching-stuff (see below) that work as expected, each mp4-file request a second time is served by the edge-cache without origin traffic.
But I want to be able to seek in the file. The functionality comes from the mp4-module. Just append the query-param "?start=120" tells nginx to serve the mp4-content starting with timestamp 120sec. This works fine with origin directly requested. But as soon as i enable mp4-module in the caching-location of the nginx, the request will be 404.
nginx.conf # origin:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/www;
location ~ \.mp4$ {
mp4;
expires max;
}
}
nginx.conf # edge:
proxy_cache_path /usr/share/nginx/cache levels=2:2 keys_zone=icdn_cache:10m inactive=7d max_size=2g;
proxy_temp_path /usr/share/nginx/temp;
proxy_ignore_headers X-Accel-Expires Cache-Control Set-Cookie;
log_format cache '[$time_local] Cache: $upstream_cache_status $upstream_addr $upstream_response_time $status $bytes_sent $proxy_add_x_forwarded_for $request_uri';
access_log /usr/local/nginx/logs/cache.log cache;
upstream origin {
server <origin-domain>;
}
server {
listen 80;
server_name localhost;
location ~ \.mp4$ {
mp4;
proxy_cache icdn_cache;
proxy_pass http://origin;
proxy_cache_key $uri;
}
}
I also tried:
location / {
location ~ \.mp4$ { mp4; }
proxy_cache icdn_cache;
proxy_pass http://origin;
proxy_cache_key $uri;
}
Is there a way to make cached mp4-files work with the seeking-function of mp4-module?
You must use proxy_store. proxy_cache will create a lot of files for every ?start=xxxx request.
To let an mp4 module seek in files you need the full movie. proxy_store will make a mirror on the cache server.
proxy_cache is part of proxy module. Currently you can't use nginx mp4 module with proxy, it only works for static files, that's it.
How can i rewrite a domain with a port to a subdomain?
e.q.: domain.com:3000 to sub.domain.com ?
thanks for your help! :)
greetz
If you actually want to redirect (301 or 302) your web traffic
You create a server {} section listening on port 3000 and you just redirect it to another server {} section that is listening on port 80. In each server {} section set the listen property appropriately.
I guess you are trying to handle the redirection within à single server section and according to this page the listen directive applies to a server context
If you want to use nginx as a proxy
Then what you are looking for is the proxy_pass directive. Here is a sample configuration extracted from an config I have to use nginx as a proxy for my rails app (thin). Basically my app runs locally (but it would also work on a remote host) on port 3200 and the relevant nginx config part looks as follow:
upstream my-app-cluster
{
server localhost:3200;
}
server
{
listen 80;
server_name mydomain.com;
root /root/to/public/folder;
access_log /my/app/log/folder/myapp.log;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://my-app-cluster;
break;
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
You could use Passenger in nginx to delivery the Ruby app - that's the method we are currently using.
http://www.modrails.com/