I've a SPA (Single Page Application) site, let's say under https://example.com and an API for it under https://api.example.com
I want to serve server rendered content for specific useragents like googlebot, facebookexternalhit, etc.
So, if user goes to https://example.com/brandon/things it will get served SPA, but if bot goes to the same URL it will get served server rendered page with all proper meta and open graph tags.
My server rendered pages with proper matching are under https://api.example.com/ssr/
So for example if bot hits https://example.com/brandon/things it should get content from https://api.example.com/ssr/brandon/things
I almost got it working with nginx proxy_pass if statement to the Django application (which returns server rendered output) but unfortunately there's one edge case that makes it behave weirdly.
My implementation:
server {
listen 80;
server_name example.com; # url of SPA
index index.html;
root /srv/example_spa/public/dist; # directory of SPA index.html
# $ssr variable that tells if we should use server side rendered page
set $ssr 0;
if ($http_user_agent ~* "googlebot|yahoo|bingbot|baiduspider|yandex|yeti|yodaobot|gigabot|ia_archiver|facebookexternalhit|facebot|twitterbot|developers\.google\.com|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|redditbot") {
set $ssr 1;
}
# location block that serves proxy_pass when the $ssr matches
# or if the $ssr doesn't match it serves SPA application index.html
location / {
if ($ssr = 1) {
proxy_pass http://127.0.0.1:9505/ssr$uri$is_args$args;
}
try_files $uri /index.html;
}
}
But there's the problem:
Everything works dandy and sweet, except one case.
User hits https://example.com/brandon/things/ and he gets SPA index.html - perfect.
User hits https://example.com/brandon/things and he gets SPA index.html - perfect.
Bot hits https://example.com/brandon/things/ and he gets server rendered page - perfect.
Bot hits https://example.com/brandon/things (without appended slash) and he gets redirected (301) to https://example.com/ssr/brandon/things - BAD BAD BAD
I've tried to make it work for couple of hours now without luck.
What would you suggest? I know if in nginx is evil, but I don't know how to make it work without it...
Any help is appreciated
You need to alter the redirects for proxy_pass
location / {
proxy_redirect http://127.0.0.1/ssr/ http://$host/ssr/;
proxy_redirect /ssr/ /;
if ($ssr = 1) {
proxy_pass http://127.0.0.1:9505/ssr$uri$is_args$args;
}
try_files $uri /index.html;
}
It turns out this was issue with my Django application redirect. I thought I had "APPEND_SLASH" option disabled, but it was enabled and made redirect when there was no slash. And it redirected without changing the host to https://api.example.com, but only URI part. Hence my confusion.
And I actually found two ways to fix that.
First, just use rewrite to append slash when there isn't one.
location / {
if ($ssr = 1) {
rewrite ^([^.]*[^/])$ $1/ permanent;
proxy_pass http://127.0.0.1:9505/ssr$uri$is_args$args;
}
try_files $uri /index.html;
}
Second, modify proxy_pass to always add / slash after $uri part and server side render application url config to accept two slashes at the end //'. It's a little hacky but has no side effects and works as it should.
Nginx config:
location / {
if ($ssr = 1) {
proxy_pass http://127.0.0.1:9505/ssr$uri/$is_args$args;
}
try_files $uri /index.html;
}
Django URL regex:
r'^ssr/(?P<username>[\w-]+)/(?P<slug>[\w-]+)(/|//)$'
New to nginx and still trying to figure out its methods.
I'm trying to do a redirect to an external URL based on the referring URL. For example, in the code below that I have for the hosted domain, if the referring URL comes from Facebook, I want to redirect the user to a specific URL:
location / {
index index.php;
if ($http_referer ~* ^(.*?(\bfacebook\b)[^$]*)$ ) {
rewrite http://www.othersite.com break;
}
try_files $uri $uri/ #handler;
expires 30d;
}
Nginx doesn't throw any errors once it's restarted, but despite testing this from a Facebook link, it's not executing.
Any nginx / regular expression gurus who can point me in the right direction?
Thanks in advance.
Although it may pass the syntax test, your rewrite statement is incorrect. To redirect any URI to a new URL you would use:
rewrite ^ http://www.example.com/? permanent;
But the preferred solution would be the more efficient:
return 301 http://www.example.com/;
See this page for details of both directives.
Trying to achieve constant language code in url's 1st segment with nginx regex location configuration and could not find the correct syntax.
Necessary result:
example.com stays example.com
example.com/en stays example.com/en
example.com/en/ stays example.com/en or example.com/en/ (don't care)
example.com/en/etc stays example.com/en/etc
example.com/etc changes to example.com/en/etc
example.com/etc/segment changes to example.com/en/etc/segment
Currently I have found out this code but it still stuck in somewhere. It makes permanent loop and doesn't not use $1 argument.
location ~ "^/(?![a-z]{2}/)(.+)$" {
rewrite / /en/$1 permanent;
}
#Using handler here for removing index.php in uri (example.com/index.php -> example.com);
location / {
index index.htm index.html index.php;
try_files $uri $uri/ #handler;
}
location #handler {
rewrite / /index.php?$query_string;
}
UPDATE:
Answer can be found in this question: https://stackoverflow.com/a/33913261/2662849
I am new to Nginx..and need to setup some redirects
We are launching our latest code, and what to redirect all instance of the sites
for eg:-
https://woi.com/movies
https://woi.com/movies/Fear
https://woi.com/videos
https://woi.com/videos/Captain
to
https://woi.tv/#!/movies
https://woi.tv/#!/movies/Fear
https://woi.tv/#!/videos
https://woi.tv/#!/videos/Captain
I have made changes in Nginx.conf file:-
Http
{
...
location /movies {
rewrite ^.* https://$woi.tv/movies permanent;
}
location /movies/ {
rewrite ^.* https://$woi.tv/movies/ permanent;
}
location /videos {
rewrite ^.* https://$woi.tv/videos permanent;
}
location /videos/ {
rewrite ^.* https://$woi.tv/videos/ permanent;
}
}
I dont know if I am right. But the above is not working.
Please help me.
If you want to redirect all unconditionally you can try something like this, not sure if the # can be written in a redirect or not, but you can try.
server {
listen 443 ssl;
server_name example.com;
root /path/to/root;
index index.php; # or whatever index
# ssl settings
location = / {
# the non redirecting settings
try_files $uri $uri/;
}
location / {
# the remaining of the site, we should redirect here
return 301 $scheme://$http_host#!$request_uri;
}
}
EDIT:
On a second thought I think this would break the assets, so I think the redirect should be as a fall back
location / {
try_files $uri #redirect;
}
location #redirect {
return 301 $scheme://$http_host#!$request_uri;
}
I try to get an / to every urls end:
example.com/art
should
example.com/art/
I use nginx as webserver.
I need the rewrite rule for this..
For better understanding check this:
http://3much.schnickschnack.info/art/projekte
If u press on a small thumbnail under the big picture it reloads and shows this url:
http://3much.schnickschnack.info/art/projekte/#0
If i now have a slash on all urls (on the end) it would work without a reload of the site.
Right now i have this settings in nginx-http.conf:
server {
listen *:80;
server_name 3much.schnickschnack.info;
access_log /data/plone/deamon/var/log/main-plone-access.log;
rewrite ^/(.*)$ /VirtualHostBase/http/3much.schnickschnack.info:80/2much/VirtualHostRoot/$1 last;
location / {
proxy_pass http://cache;
}
}
How do I configure nginx to add a slash? (I think i should a rewrite rule?)
More likely I think you would want something like this:
rewrite ^([^.]*[^/])$ $1/ permanent;
The Regular Expression translates to:
"rewrite all URIs without any '.' in them that don't end with a '/' to the URI + '/'"
Or simply:
"If the URI doesn't have a period and does not end with a slash, add a slash to the end"
The reason for only rewriting URI's without dots in them makes it so any file with a file extension doesn't get rewritten. For example your images, css, javascript, etc and prevent possible redirect loops if using some php framework that does its own rewrites also
Another common rewrite to accompany this would be:
rewrite ^([^.]*)$ /index.php;
This very simply rewrites all URI's that don't have periods in them to your index.php (or whatever file you would execute your controller from).
rewrite ^([^.\?]*[^/])$ $1/ permanent;
to avoid querystrings of a rest url getting a / tagged on.
e.g.
/myrest/do?d=12345
For nginx:
rewrite ^(.*[^/])$ $1/ permanent;
Odd that this is the first result in Google, but doesn't have a satisfactory answer. There are two good ways to do this I know of. The first is to straight-up check if the request will hit a file and only apply a rewrite condition if not. E.g.
server {
# ...
if (!-f $request_filename) {
rewrite [^/]$ $uri/ permanent;
}
location / {
# CMS logic, e.g. try_files $uri $uri /index.php$request_uri;
}
# ...
}
The second, which many prefer as they'd rather avoid any use of if that isn't 100% necessary, is to use try_files to send the request to a named location block when it won't hit a file. E.g.
server {
# ...
location / {
try_files $uri $uri/ #cms;
}
location #cms {
rewrite [^/]$ $uri/ permanent;
# CMS logic, e.g. rewrite ^ /index.php$request_uri;
}
# ...
}
it's too late but I want to share my solution, I've met issue with trailing slash and nginx.
#case :
# 1. abc.com/xyz => abc.com/xyz/
# 2. abc.com/xyz/ => abc.com/xyz/
# 3. abc.com/xyz?123&how=towork => abc.com/xyz/?123&how=towork
# 4. abc.com/xyz/?123&ho=towork => abc.com/xyz/?123&how=towork
and this is my solution
server {
....
# check if request isn't static file
if ($request_filename !~* .(gif|html|jpe?g|png|json|ico|js|css|flv|swf|pdf|xml)$ ) {
rewrite (^[^?]+[^/?])([^/]*)$ $1/$2 permanent;
}
....
location / {
....
}
}
server {
# ... omissis ...
# put this before your locations
rewrite ^(/.*[^/])$ $1/ permanent;
# ... omissis ...
}
If you want some kind of requests (say other than GET ones) to be prevented from doing this (usually it's about POST requests, as rewrite turns any request method into GET, which may break some of your site's dynamic functionality), add an if clause:
server {
# ... omissis ...
# put this before your locations
if ($request_method = "GET" ) {
rewrite ^(/.*[^/])$ $1/ permanent;
}
# ... omissis ...
}
You can also put the rewrite in a location block (if too), to make it more specific.
using the rewrites from anthonysomerset in a Wordpress, I experimented problems accesing to /wp-admin dashboard due to reirection loop. But i solve this problem using the above conditional:
if ($request_uri !~ "^/wp-admin")
{
rewrite ^([^.]*[^/])$ $1/ permanent;
rewrite ^([^.]*)$ /index.php;
}
If nginx behind proxy with https, this snippet do correct redirect for $scheme
map $http_x_forwarded_proto $upstream_scheme {
"https" "https";
default "http";
}
server {
...
location / {
rewrite ^([^.\?]*[^/])$ $upstream_scheme://$http_host$1/ permanent;
}
...
}
And on the upstream proxy pass the X-Forwarded-Proto header like:
location / {
proxy_set_header X-Forwarded-Proto $scheme;
...
}
This rule solves query string case too:
location ~ ^/([^.]*[^/])$ {
if ($query_string) {
return 301 $scheme://$host/$1/?$query_string;
}
return 301 $scheme://$host/$1/;
}
The regex has taken from #marc's answer:
rewrite ^([^.\?]*[^/])$ $1/ permanent;
The extra slash ^/ in regex is added to improve readability
Try this: ^(.*)$ http://domain.com/$1/ [L,R=301]
This redirects (Status code 301) everything ($1) without a "/" to "$1/"