Nodejs Express POST returns 404 error on https Xampp Apache server while proxied - html

I'm running a Xampp Apache server on Windows and I'm trying to simply pass a value from a slider to my Node.js server. To do that I use the fetch method and post my values to /t/request.
If I replace "/t/request" with "http:localhost:3000/t/request" everything works fine locally, but not on any other machine, otherwise I get the "POST https://mywebsite.com/t/request 404 not found" error.
I am (to my knowledge) proxying Node.js in the Apache configs, so I can't see why I get this error whatever I do.
I have read that you need to set up SSL certificates for your Node.js server too, but I don't see why since Apache and Node.js communicate locally. Other people have said the same thing.
UPDATE: Changing http to https in the vhosts config file for proxying changes the error from a 404 to a 500.
My html js code that loads up on /t/imz7hyza
var sliderOutput = document.getElementById("rangeSlider");
sliderOutput.oninput = async function(){
try {
let obj = JSON.stringify({sliderVal: sliderOutput.value, buttonVal: buttonOutput.checked})
let obj2 = JSON.parse(obj)
const response = await fetch('/t/request', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' },
body: obj,
});
console.log(obj)
} catch(err) {
console.error(`Error: ${err}`);
}
}
My server.ts code
import express, { NextFunction, Request, Response } from "express";
import cors from "cors";
import bodyParser from "body-parser";
const app = express();
var jsonParser = bodyParser.json();
app.use(express.json());
app.use(cors({origin: '*'}));
app.use(express.static('public'));
interface IUpdateSlider{
sliderVal: number
buttonVal: boolean
}
app.post("/t/request", async (req: Request<{}, IUpdateSlider, IUpdateSlider>, res: Response) => {
try {
let number = req.body.sliderVal
if(req.body.buttonVal) console.log(number, "on")
else console.log(number, "off")
res.status(200).send({
msg: "Read!"
})
res.end();
} catch (error) {
console.error(error);
res.status(500).send({
msg: "Internal server error."
})
}
})
app.listen(3000, () => {
console.log("server listening to port 3000");
})
My Apache httpd.conf. I have proxy, proxy_http, proxy_ajp, rewrite, deflate, headers, proxy_balancer, proxy_connect and proxy_html enabled, along with the required ssl modules.
Define SRVROOT "C:/xampp/apache"
ServerRoot "C:/xampp/apache"
ProxyPass /t/imz7hyza http://localhost:3000/
Listen 80
--modules here
ServerName localhost
<Directory />
AllowOverride none
Require all denied
</Directory>
DocumentRoot "C:/xampp/htdocs"
...
<VirtualHost *:443>
ServerAdmin mymail#gmail.com
DocumentRoot "C:/xampp/htdocs"
ServerName www.mywebsite.com
ErrorLog "logs/error.log"
SSLEngine on
SSLCertificateFile "conf/ssl.crt/mywebsite_com.crt"
SSLCertificateKeyFile "C:/xampp/apache/bin/server.key"
SSLCertificateChainFile "conf/ssl.crt/mywebsite_com.ca-bundle"
</VirtualHost>
My .httaccess file
ErrorDocument 404 /404.html
DirectoryIndex /index.html
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
And finally my httpd-vhosts.conf file
<VirtualHost *:80>
ServerName www.mywebsite.com
Redirect permanent / https://mywebsite.com/
ProxyPass /t/request https://localhost:3000
ProxyPassReverse /t/request https://localhost:3000
</VirtualHost>
<VirtualHost *:443>
DocumentRoot "C:/xampp/htdocs/"
ServerName www.mywebsite.com
SSLEngine on
SSLCertificateFile "conf/ssl.crt/mywebsite_com.crt"
# SSLCertificateKeyFile "conf/ssl.key/server.key"
SSLCertificateKeyFile "C:/xampp/apache/bin/server.key"
<Directory "C:/xampp/htdocs/">
Options All
AllowOverride All
Require all granted
</Directory>
ProxyPass /t/request https://localhost:3000
ProxyPassReverse /t/request https://localhost:3000
</VirtualHost>

Related

nginx error pages based on accept header do not work for json requests

I'm trying to build a nginx-based maintenance mode application, that catches all requests to my applications and returns a predefined response as a 503.
I currently have applications requesting json responses as well as users accessing the pages with their browsers. So in case the request contains the header Accept: application/json, I want to respond with the content of a json file maintenance.json, otherwise with an html file maintenance.html.
My current nginx config looks like this:
server {
listen 8080;
root /usr/share/nginx/maintenance;
server_tokens off;
error_page 503 = #unavailable;
location ^~ / {
return 503;
}
location #unavailable {
set $maintenanceContentType text/html;
set $maintenanceFile /maintenance.html;
if ($http_accept = 'application/json') {
set $maintenanceContentType application/json;
set $maintenanceFile /maintenance.json;
}
default_type $maintenanceContentType;
try_files $uri $maintenanceFile;
}
}
For browser requests to any path this works out fine: "https://maintenance.my-domain.local/some-path". I get the response code and the html content.
But for requests with header Accept: application/json I get a 404 html page. And the nginx log shows [error] 21#21: *1 open() "/usr/share/nginx/maintenance/some-path" failed (2: No such file or directory), client: 10.244.2.65, server: , request: "GET /asd HTTP/1.1", host: "maintenance.my-domain.local".
It seems like json requests are ignoring my location for some reason. When I remove the directive to set the appropriate file and just always return the html this also works for json-requests.
Anyone any idea?
I'm not necessarily looking for a fix for this specific config, but rather for something that fits my needs of responding with different "error pages" based on the Accept header.
Thanks in advance!
EDIT: For some reason this now results in an HTTP 200 instead of a 503. Don't know what I changed..
EDIT2: Managed to fix a part of it:
server {
listen 8080;
root /usr/share/nginx/maintenance;
server_tokens off;
location ^~ / {
if ($http_accept = 'application/json') {
return 503;
}
try_files /maintenance.html =404;
}
error_page 503 /maintenance.json;
location = /maintenance.json {
internal;
}
}
With this config I now get the maintenance page when using the browser and the maintenance json, when defining the header Accept: application/json. The browser response code is 200 now though...
Ok, I found the solution to my problem.
# map the incoming Accept header to a file extension
map $http_accept $accept_ext {
default html;
application/json json;
}
server {
listen 8080;
root /usr/share/nginx/maintenance;
server_tokens off;
# return 503 for all incoming requests
location ^~ / {
return 503;
}
# a 503 redirects to the internal location `#maintenance`. the
# extension of the returned file is decided by the Accept header map
# above (404 in case the file is not found).
error_page 503 #maintenance;
location #maintenance {
internal;
try_files /maintenance.$accept_ext =404;
}
}
Key was the map on the top. I just added application/json there and mapped everything else to the html file by default. But you could add multiple other files/file types there of course.

Angular 8 + Nginx - failed to load resource

I have an application written in Angular 8 that I'm serving on nginx. After the deployment I get an following error:
Failed to load resource: the server responded with a status of 404 ()
Additionally in the Dev Tools we can see such a message:
error: "404 Not Found404 Not Foundnginx/1.12.0
message: "Http failure response for https://myUrl/app/assets/config/endpoints.json: 404 OK
name: "HttpErrorResponse" ok: false status: 404 statusText:
"OK" url: "https://myUrl/app/assets/config/endpoints.json"
What I didn't understand why is the response stating "OK"? But it's not the main problem. The main problem is that I can't load this endpoints.json file neither access any file located under the assets dir.
When I directly go to the URI:
https://myUrl/app/someFile.png
then it's visible, but if I try to reach
https://myUrl/app/assets or https://myUrl/app/assets/logo.png
then i get an 404 error page.
It's served from a docker container and I saw that all files are there so the structure is as follows in
/usr/share/nginx/html/
-/
-index.html
-assets/
-------config/
-------------endpoints.json
-------images/
-------------logo.png
-polifills.js
-favicon.ico
The app was build with the --prod flag and the base href flag --base-href ./
As I see the favicon is loading and all the scripts in the main dir work fine. The problem is related to the assets dir...
Here are my files:
* nginx.conf
server {
listen 8888;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
try_files $uri $uri/ /index.html;
}
}
Enpoints service (which is trying to get this endpoints.json
private enpoints$: Observable<Endpoint>;
private readonly ENDPOINT_URL = './assets/config/endpoints.json';
constructor(private http: HttpClient) {}
public loadEndpoints(): Observable<Endpoint> {
if (!this.enpoints$) {
this.enpoints$ = this.http.get<Endpoint>(this.ENDPOINT_URL).pipe(shareReplay(1));
}
return this.enpoints$;
}
Any suggestions?
Just figured it out. The issue was related to my nginx configuration.
I had to take the root path out of the location block so it's applicable to all locations. The official nginx pitfalls doc helped me here a lot.
Now my config file looks like that:
server {
listen 8888;
server_name localhost;
root /usr/share/nginx/html;
location / {
index index.html index.htm;
include /etc/nginx/mime.types;
try_files $uri $uri/ /index.html;
}
location /assets {
try_files $uri $uri/ /index.html;
}
}
Although I think the second block isn't needed at all. I have to check it, and will reply.

Form submission to Mailchimp using Vue.js and Axios results in CORS error

I have a component in my Vue.js app that submits email from a form to Mailchimp using Axios.
I've read that to get around CORS in Mailchimp's post URL, I need to use the post-json version and add &c=? to the end of the URL. I've also updated my request method from POST to GET and serialized my form input.
Component.vue
<mailchimp-subscribe action="https://example.us15.list-manage.com/subscribe/
post-json?u=xxx&id=xxx&c=?"></mailchimp-subscribe>
MailchimpSubscribe.vue
<template>
<form #submit.prevent="subscribe">
<input v-model="email" type="email" name="EMAIL" value="" placeholder="Email Address" required>
<input class="button" type="submit" value="Subscribe">
</form>
</template>
<script>
import axios from 'axios'
export default {
name: 'MailchimpSubscribe',
props: {
action: {}
},
data: () => ({
email: '',
response: {}
}),
methods: {
subscribe: function (event) {
axios({
method: 'get',
url: this.action,
data: JSON.stringify(this.email),
cache: false,
dataType: 'json',
contentType: 'application/json; charset=utf-8'
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
}
}
}
</script>
Using the code above, I still get the following error:
Failed to load https://example.us15.list-manage.com/subscribe/post-json?u=xxx&id=xxx&c=?: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
Is there a step I'm missing?
Your server (backend) must respond with exactly the same header.
Namely: https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
For example Axios get/post (never mind):
const configAxios = {
headers: {
'Content-Type': 'application/json',
},
};
axios.post('api/categories', configAxios)
.then((res) => {
this.categories = res.data;
console.log(res);
})
.catch((err) => {
console.warn('error during http call', err);
});
For example server-side. I like Symfony4 and it is used NelmioCorsBundle, look at the allow_origin: ['*']. It's pretty simple, if you use Symfony.
nelmio_cors:
defaults:
allow_credentials: false
allow_origin: ['*']
allow_headers: ['Content-Type']
allow_methods: []
expose_headers: []
max_age: 0
hosts: []
origin_regex: false
forced_allow_origin_value: ~
paths:
'^/api/':
allow_origin: ['*']
allow_headers: ['X-Custom-Auth', 'Content-Type', 'Authorization']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
max_age: 3600
'^/':
origin_regex: true
allow_origin: ['^http://localhost:[0-9]+']
allow_headers: ['X-Custom-Auth', 'Content-Type']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
max_age: 3600
hosts: ['^api\.']
If you are not directly working with the server, then check with your supplier for this nuance.
This header can also be transmitted for example through Nginx, which is not the best idea.
For example, look at the:
add_header Access-Control-Allow-Origin *;
server {
listen 8080;
server_name site.local;
root /var/www/site/public;
location / {
add_header Access-Control-Allow-Origin *;
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/index.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/project_error.log;
access_log /var/log/nginx/project_access.log;
}
It is worth paying attention if there is no data passed it removes the Content-Type. The data must always be transmitted or be null. This is strange and it is misleading.

How to returns index.html as top domain not /index.html on Nginx?

I tried below nginx config:
server {
listen 80;
server_name mysite.com;
location / {
root /var/www
index index.html;
try_files $uri.html $uri #proxy;
access_log off;
}
...
}
But I couldn't access to my site with /. I can access to my site with /index or /index.html though.
How can I access to mysite with /?

Configuring nginx for single page website with HTML5 push state URL's

How can I configure nginx to redirect all URL's (not prepended with /api or some static resource eg. JS/images) to index.html? Reason is I am using HTML5 push state URL's with a single page application. Meaning content is changed whether AJAX or JS depending on the URL
My current nginx config looks like:
server {
listen 2000;
server_name localhost;
location / {
root /labs/Projects/Nodebook/public;
index index.html;
}
location /api/ {
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_set_header X-NginX-Proxy true;
proxy_pass http://localhost:3000/;
proxy_redirect off;
}
}
location / {
try_files $uri /index.html;
}
This will check if the requested file exists and return it. If the file doesn't exist, it will return index.html.
http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files
mattes answer is almost a solution, however it won't give 404 for missing files (e.g. favicon.icon) as aschepis pointed out.
Nginx will pick the first location that matches. So we can first match for files (which will give 404 if the file does not exist). And after put a location which defaults to index.html for all urls.
location /.+\..+ { # files (assuming they always have a dot)
# use eg alias to serve some files here
}
location / { # url routed by client, client gives 404 for bad urls
try_files $uri /index.html;
}
You need to add to your nginx config file:
rewrite ^(.+)$ /index.html last;
Then say you're using Backbone.js just make sure you re-route any non-defined route to a 404 page:
routes: {
// Other routes
"*path" : "notFound"
},
notFound: function(path) {
// Load 404 template, probably of a cute animal.
}
Source:
http://readystate4.com/2012/05/17/nginx-and-apache-rewrite-to-support-html5-pushstate/