I'm statically serving a PWA builded with Create-React-App through Nginx.
This is my Nginx location configuration:
location / {
root /etc/my_app/latest;
index index.html;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
expires off;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept' always;
allow all;
}
So :
When querying https://my_domain_name_app/ it logically find the index.html file in /etc/my_app/latest/index.html
When querying https://my_domain_name_app/assets/image.png it logically find the image.png file in /etc/my_app/latest/assets/image.png
But when my PWA's URL is, for example, https://my_domain_name_app/login, Nginx will try to find a file login in /etc/my_app/latest/ and throw a 404 error instead of accessing the /login page of my application
How can I tell Nginx to differenciate the page of my application from the files I serve ?
The answer of Richard Smith is correct, I had to add try_files $uri $uri/ /index.html;
Related
I am writing a backend for my Flutter app using Aqueduct. I have Aqueduct set up so that Nginx proxies requests to it like this:
server {
root /home/web/my_server/web;
index index.html index.htm;
server_name example.com www.example.com;
location / {
try_files $uri $uri/ =404;
}
location /api {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.0.1:8888;
proxy_http_version 1.1;
}
...
}
In Aqueduct I serve the files using a FileController:
router.route("/api/v1/app/*")
.link(() => LogController(context))
.link(() => FileController("public/app/")
..setContentTypeForExtension('apk', ContentType('application', 'vnd.android.package-archive')));
However, any files that it returns don't include the Content-Length header. That means that I can't show download progress.
I tried creating a custom FileController where I added the headers in manually:
final contentLengthValue = file.lengthSync();
return Response.ok(byteStream,
headers: {HttpHeaders.lastModifiedHeader: lastModifiedDateStringValue,
HttpHeaders.contentLengthHeader: contentLengthValue,
'x-decompressed-content-length': contentLengthValue,
HttpHeaders.cacheControlHeader: 'no-transform',
HttpHeaders.acceptRangesHeader: 'bytes'})
..cachePolicy = _policyForFile(file)
..encodeBody = false
..contentType = contentType;
The Content-Length header was still removed, but the x-decompressed-content-length header remained so this is a possible workaround. It just doesn't play nicely with some Flutter plugins that look for the Content-Length header and don't have a convenient way to check other headers.
Is this an Aqueduct problem or an Nginx problem? How do I solve it?
This solution works, but it skirts around the original problem. That it, it allows you to serve files that have the Content-Length in the header, but it doesn't explain why it was getting stripped in Aqueduct. Other answers are welcome.
Rather than have Aqueduct serve files, just have Nginx serve them directly.
If you can't change your API route, you can just give it an alias in the Nginx config location block. Add this before the /api location block.
location /api/v1/app/ {
alias /home/web/my_server/public/app/;
}
Now files in the app/ folder will get served by Nginx rather than Aqueduct. Nginx includes the Content-Length header in the files it returns.
I'm trying to use the Woocommerce (v 3.5.4) Rest Api on my VPS (debian 9, Nginx).
Everything works well in my local machine (windows 10, XAMPP).
wpbop/ is the folder (var/www/wpbop/) where the wordpress files are stored.
The next basic URL in a browser should send the endpoints of the API (no need of athentication for this first step) :
http://my-public-ip/wpbop/wp-json/wc/v3
Or a curl in command line
curl http://127.0.0.1/wpbop/wp-json/wc/v3
in both cases, i get error 404 Not Found.
I can acces to the blog / admin blog without any problems ( http://my-public-ip/wpbop )
My permalinks are set on "Postname" in wordpress admin panel, this is recommanded by many people in same case.
EDIT - SOLUTION :
Since my Wordpress installation is in a sub-domain,
try_files $uri $uri/ /index.php$is_args$args;
can't find index.php. Just change this line by :
try_files $uri $uri/ /wpbop/index.php$is_args$args;
and it works !
Perhaps problem is coming from my Nginx conf file ?
server {
server_name localhost;
listen 80;
root /var/www;
location /wpbop {
index index.php;
access_log /var/log/nginx/blog.access.log;
error_log /var/log/nginx/blog.error.log;
try_files $uri $uri/ /index.php$is_args$args;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
fastcgi_pass 127.0.0.1:7000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
}
}
I tried many things without any results, and I'm stuck for several days. Can someone help me ?
Thanks for reading me.
This case need a simple fix in the NGINX configuration file. This is related to the path of my wordpress installation.
Since my Wordpress installation is in a sub-domain,
try_files $uri $uri/ /index.php$is_args$args;
--> can't find index.php. Just change this line by :
try_files $uri $uri/ /wpbop/index.php$is_args$args;
when you get 404 code. try to access http://yoursite/?rest_route=/wp/v2/posts
Official documents https://developer.wordpress.org/rest-api/key-concepts
Move root /var/www/; by one level up (to server context). It is not being inherited.
We’re trying to setup a 3scale platform over OpenShift to manage API access between a REST service and a JavaScript web application. Authentication shall be managed with the user-key placed in a HTTP header.
The two applications are reachable on different URLs:
JS web application: http://siteA.example.com
REST API application: http://siteB.example.com
so we are using CORS to implement cross-origin resources on the webapp. This is introducing several OPTIONS pre-flight requests sent by the browser without the user-key header, thus receiving an HTTP 403 error from 3scale.
Is there a way to avoid this behaviour?
If you can't handle it at the application level then you can do a nginx if statement to handle it.
location / {
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin "http://example.com";
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization";
add_header Access-Control-Allow-Credentials "true";
...
add_header Content-Length 0;
add_header Content-Type text/plain;
return 200;
}
...
}
Via http://blog.rogeriopvl.com/archives/nginx-and-the-http-options-method/
I had the same issue in 3scale when using the AWS AMI and was able to solve it by adding the app_key and app_id to the allowed headers for an options request.
In testing the request worked in postman but would not work via Chrome. In my case when a browser issued the preflight options check it resulted in rejection because the app_key and app_id header are not allowed by the CORS defaults.
Adding support for those headers can be achieved by adding an entry for them to the end of the 'Access-Control-Allow-Headers' header. I made this configuration a separate file named cors.conf:
#### CORS ####
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,app_id,app_key';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
#### CORS END ####
The cors.conf was then included under any "location /" block in nginx.conf:
...
location / {
include /opt/openresty/nginx/conf/cors.conf;
set $provider_key null;
set $cached_key null;
...
Once this change was made my browser was able to successfully make requests.
The CORS on Nginx document was used as a baseline and I noted that only the OPTIONS section needed to be modified to get the desired results.
When the environment variable APICAST_PATH_ROUTING is set to true, and APIcast is exposing multiple Services, if the CORS Policy is not enabled for all the Services, the preflight OPTIONS request will receive a 403 (Forbidden) response.
Set the CORS policy on all the services/apis within your 3scale.
More info from RedHat:
https://issues.jboss.org/browse/THREESCALE-3063
3scale gateway now supports out-of-the-box CORS policy definition. More information here: https://access.redhat.com/documentation/en-us/red_hat_3scale_api_management/2.8/html/administering_the_api_gateway/apicast_policies#cors
I'm trying to build a single page app that utilizes HTML5 App Cache, which will cache a whole new version of the app for every distinct URL, thus I must redirect everyone to / and have my app route them afterward (this is the solution used on devdocs.io).
Here's my nginx config. I want all requests to send a file if it exists, redirect to my API at /auth and /api, and redirect all other requests to index.html. Why is the following configuration causing my browser to say that there is a redirect loop? If the user hits location block #2 and his route doesn't match a static file, he's sent to location block #3, which will redirect him to "/" which should hit location block #1 and serve index.html, correct? What is causing the redirect loop here? Is there a better way to accomplish this?
root /files/whatever/public;
index index.html;
# If the location is exactly "/", send index.html.
location = / {
try_files $uri /index.html;
}
location / {
try_files $uri #redirectToIndex;
}
# Set the cookie of the initialPath and redirect to "/".
location #redirectToIndex {
add_header Set-Cookie "initialPath=$request_uri; path=/";
return 302 $scheme://$host/;
}
# Proxy requests to "/auth" and "/api" to the server.
location ~* (^\/auth)|(^\/api) {
proxy_pass http://application_upstream;
proxy_redirect off;
}
That loop message suggests that /files/whatever/public/index.html doesn't exist, so the try_files in location / doesn't find $uri when it's equal to /index.html, so the try_files always internally redirects those requests to the # location which does the external redirect.
Unless you have a more complicated setup than you've outlined, I don't think you need to do so much. You shouldn't need external redirects (or even internal redirects) or server-side cookie sending for a one-file js app. The regex match for app and api wasn't quite right, either.
root /files/whatever/public;
index index.html;
location / {
try_files $uri /index.html =404;
}
# Proxy requests to "/auth" and "/api" to the server.
location ~ ^/(auth|api) {
proxy_pass http://application_upstream;
proxy_redirect off;
}
I was writing a RESTful applicaiton in PHP and enabled DELETE, PUT requests for nginx.
location / {
root html;
index index.php index.html index.htm;
dav_methods PUT DELETE;
}
When I executed a REST Request with method DELETE, which I wanted to handle inside my index.php - nginx removed the html folder.
What is the right way to tell nginx to pass DELETE requests to my index.php ?
Nginx does not disable PUT or DELETE requests, but it does not allow these requests on a folder index. There isn't really anything that needs to be enabled with nginx (you should remove the dav_methods line), but you need to avoid accessing your index.php through the index directive like:
index index.php index.html index.htm;
Instead use try_files to match the index.php file ie like:
try_files $uri /index.php$is_args$args;
In this case nginx wont complain about your DELETE method.
NginX executes those HTTP methods (DELETE, PUT) directly without even invoking PHP engine because they are handled by the DAV extension inside nginX.
To overcome the issue, you may use POST HTTP method for all of your API calls, but add additional custom header to indicate the actual REST method - instead of this
PUT /api/Person/4 HTTP/1.1
Host: localhost:10320
Content-Type: application/json
Cache-Control: no-cache
you will invoke this
POST /api/Person/4 HTTP/1.1
Host: localhost:10320
Content-Type: application/json
X-REST-Method: PUT
Cache-Control: no-cache
and then in PHP you will check in this way
if($_SERVER['HTTP_X_REST_METHOD']!='')
switch($_SERVER['HTTP_X_REST_METHOD'])
{
case 'PUT':
...
break;
case 'PATCH':
...
break;
case 'DELETE':
...
break;
}