So I've got an angular app that has a few routes that I can easily get to.
However, /admin
eludes me as I get a 403 error. An example of a working route, set up the exact same way at /articles
loads just fine.
Here's my app config:
app.config(['$stateProvider', '$locationProvider', '$urlRouterProvider',
function($stateProvider, $locationProvider, $urlRouterProvider) {
$stateProvider.
...
state('articles', {
url: '/articles',
templateUrl: 'components/articles/templates/articles.tmpl.html',
controller: 'ArticleController'
}).
state('articles/:id', {
url: '/articles/:id',
templateUrl: 'components/articles/templates/single-article.tmpl.html',
controller: 'ArticleController'
}).
state('admin', {
url: '/admin',
templateUrl: 'admin/templates/admin.tmpl.html',
controller: 'AdminController'
}).
state('admin.create', {
url: '/create',
templateUrl: 'admin/create/templates/create.tmpl.html',
controller: 'AdminCreateController'
}).
...
$urlRouterProvider.otherwise('/');
$locationProvider.html5Mode(true);
}
]);
I know the culprit is Nginx, and I know it's triggered with html5Mode; so I checked out how I'm re-routing to the index page and it all looks good (and works properly on the site) with this as my nginx sites-available conf:
server {
listen 80;
server_name app.local.dev;
root "/vagrant/app";
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
Here's an example of of error log for Nginx:
2015/02/06 06:14:23 [error] 3361#0: *20 directory index of "/vagrant/app/admin/" is forbidden, client: 192.168.99.1,
I don't appear to have any permissions oddities with my file structure (the root folder where /admin
files live:
-rw-r--r-- 1 vagrant vagrant 229 Feb 2 20:33 AdminController.js
-rw-rw-r-- 1 vagrant vagrant 90 Feb 2 05:18 AdminModule.js
drwxr-xr-x 1 vagrant vagrant 204 Feb 3 02:55 create
-rw-r--r-- 1 vagrant vagrant 6148 Feb 2 16:08 .DS_Store
drwxr-xr-x 1 vagrant vagrant 170 Feb 3 05:12 edit
drwxr-xr-x 1 vagrant vagrant 102 Feb 2 20:33 templates
/admin
template file:
-rw-r--r-- 1 vagrant vagrant 151 Feb 2 20:33 admin.tmpl.html
Root folder where /articles
files live:
-rw-r--r-- 1 vagrant vagrant 715 Feb 5 03:47 ArticleController.js
-rw-r--r-- 1 vagrant vagrant 94 Dec 3 18:29 ArticleModule.js
drwxr-xr-x 1 vagrant vagrant 136 Feb 6 04:19 templates
/articles
templates files:
-rw-r--r-- 1 vagrant vagrant 526 Feb 6 04:19 articles.tmpl.html
-rw-r--r-- 1 vagrant vagrant 453 Feb 5 22:35 single-article.tmpl.html
So things are re-routing and writing correctly. I can hit my main domain, get my homepage without a hash and click around and maintain a pretty url that contains no hash (html5Mode) and (aside from /admin
) be able to hit refresh and re-load that state correctly through Angular.
However, if I change in my app.config
the admin state to a url of /admins
it will magically work, reload will refresh the /admins
correctly and show me the right view.
state('admin', {
url: '/admins', // works as expected and loads what was my /admin view with correct refreshing in Nginx
templateUrl: 'admin/templates/admin.tmpl.html',
controller: 'AdminController'
}).
So is there some sort of weird reserved URL in Nginx? I'm using Vagrant with this and have specifically turned off sendfile as many report odd caching. Is my nginx.conf set up wrong? I feel like my URL rewriting is working correctly since it's working everywhere else on the site. The only other difference between a route that works /articles
and one that doesn't /admin
is that /admin
has nested states inside of it, eg. /admin/create/pages/
.
It seems your logical path clashes with your "physical" path.
UiRouter intercepts requests on the client. Every time you try to get "foo.bar/admin" it checks on a routing table and says "uhm, this is registered as a logical path (client side), so it doesn't need to query the server". The problem is, when you request directly "foo.bar/admin" instead of navigating to it, nginx is the first to get the request (since uiRouter would't even be loaded), and since you are using the same path in your server for storing files, instead returning the index.html file, your other
rules try_files $uri $uri/
has precedence and attempts to find it on your server. But of course, it doesn't find it because there is no 'admin' file there. I believe it's not a 404 because the path actually exists on the server, but is a directory and therefore it cannot return it, so it sens a 403 instead.
One possible solution is just avoid path collision, like you did when renaming 'admin' to 'admins'. Since nginx doesn't find 'admins' on the server, it fallbacks to index.html.
Other solution would be to use something like gulp or grunt to compress your files and avoid having several directories (and therefore path collision).
And last, you could rewrite some rule in order to redirect /admin to index.html... but I would't go with that. The other two are far better options.
Sadly I don't have much experience with angular, but I could say that when you request a url like /admin
nginx checks the rules you specified in the try_files
, which are
$uri # check if there's a file called admin
of course you don't have one so it goes to the next step
$uri/ #check if there's a directory called admin
And it's found, and since you only supplied a directory so it goes to check the index from
index index.html;
so nginx trys to find /admin/index.html
and I assume it doesn't exist, so then nginx falls back to the last thing which is directory listing, but since you don't have autoindex on
then it falls back to a 403
error because directory index is not allowed.
If you want to skip this step and serve index.html
I assume it has some code that is related to angular, then my suggested solution would be removing the $uri/
part entirely, so the final try_files
line would be
try_files $uri /index.html;