Just to let you know I’ve manage on how to install n8n under a subpath integrating securely under my existing app.
Let’s have an existing app such as:
frontend.mydomain.com: your frontend app
api.mydomain.com: your api
webhooks.mydomain.com: I have other webhooks for other services
Your case might be simpler, having all in one or just frontend an api, just adapt to your needs.
Authentication on API is done by an Authentication header token.
In this scenario I’d like to run n8n under frontend.mydomain.com/workflows with the same auth token, but just the UI, I want to get Access to webhooks from webhooks.mydomain.com/workflows
How?
I use nginx on api, frontend and webhooks.
Let’s suppose you already have n8n up and running.
server {
...bla bla bla....
#block access to webhooks from frontend
location ^~ /workflows/webhook {
return 404;
}
#proxy pass /workflows to n8n deployment
location /workflows {
sub_filter_once off;
sub_filter_types *;
sub_filter 'VUE_APP_URL_BASE_API:"/"' 'VUE_APP_URL_BASE_API:"/workflows/"';
sub_filter 'base:"/"' 'base:"/workflows/"';
sub_filter "src=/" "src=/workflows/";
sub_filter "href=/" "href=/workflows/";
sub_filter "BASE_URL:\"/\"" "BASE_URL:\"/workflows/\"";
rewrite ^/workflows/(.*)$ /$1 break;
proxy_set_header Accept-Encoding "";
proxy_pass http://n8n;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
}
}
So, web have two ways rewriting. With sub_filters we replace in all responses (in the code, html, js…) the different patterns we have to change to allow the VUE app to work. So, instead of the hardcoded /js/…. links we get /workflows/js/…
Then, inside the appXXX.js we change the hardcoded URLs from / to /workflows too.
If everything goes right, your installation is working under /workflows/.
-Under webhooks.mydomain.com nginx:
server{
...bla bla bla...
location /workflows/webhook {
rewrite ^/workflows/(.*)$ /$1 break;
proxy_set_header Accept-Encoding "";
proxy_pass http://n8n;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
}
location ^~ /workflows {
return 404;
}
}
We proxy pass everything on webhooks to n8n, removing /workflows/ and deny access to ui.
This way we should have everything working without securing UI. How to do that?
Nginx again
-Add to your frontend nginx:
server{
...bla bla bla....
location = /auth {
internal;
proxy_pass http://api/v1/users/auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https";
if ($http_cookie ~* "mycookiesession=([^;]+)(?:;|$)") {
set $token "Bearer $1";
}
proxy_set_header Authorization $token;
}
location /workflows {
#add this
auth_request /auth;
#the same as before
}
}
This is how it works:
-When a user logs into my frontend app the browser gets an auth token and generates a cookie with this token.
-When the user goes to frontend.mydomain.com/workflows the cookie goes too.
-With auth_request /auth; nginx authenticates each request to /auth internally, and this proxies the request to my api, transforming the cookie into the Authorization header and authenticates against http:///api/v1/users/auth. If it returns http200, user is allowed, otherwhise, rejected. You can improve the rejection with:
error_page 401 = @error401;
location @error401 {
return 302 https://frontend.mydomain.com/login;
}
This way any unauthenticated acces is redirected to login.
Full nginx config for frontend.
server {
listen 80;
server_name localhost;
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
error_page 401 = @error401;
location @error401 {
return 302 https://frontend.mydomain.com/login;
}
location ^~ /workflows/webhook {
return 404;
}
location = /auth {
internal;
proxy_pass http://api/v1/users/auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https";
if ($http_cookie ~* "mycookiesession=([^;]+)(?:;|$)") {
set $token "Bearer $1";
}
proxy_set_header Authorization $token;
}
location /workflows {
auth_request /auth;
sub_filter_once off;
sub_filter_types *;
sub_filter 'VUE_APP_URL_BASE_API:"/"' 'VUE_APP_URL_BASE_API:"/workflows/"';
sub_filter 'base:"/"' 'base:"/workflows/"';
sub_filter "src=/" "src=/workflows/";
sub_filter "href=/" "href=/workflows/";
sub_filter "BASE_URL:\"/\"" "BASE_URL:\"/workflows/\"";
rewrite ^/workflows/(.*)$ /$1 break;
proxy_set_header Accept-Encoding "";
proxy_pass http://n8n;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location ~ /\.ht {
deny all;
}
}
Hope you find it usefull