Keywords: AngularJS | HTML5 mode | URL rewriting
Abstract: This article provides an in-depth analysis of the common issue where page reloads cause wrong GET requests in AngularJS applications with HTML5 mode enabled. It explains the necessity of server-side URL rewriting by contrasting browser direct requests with Angular's client-side routing. Drawing from best practices, it details configuration steps for various server environments including Apache, Node.js/Express, and BrowserSync/Gulp. The core insight lies in understanding the collaboration between Angular's single-page application architecture and server-side routing mechanisms.
Enabling HTML5 mode in AngularJS applications removes hash symbols (#) from URLs, providing cleaner URL structures and enhanced user experience. However, developers often encounter a common issue: when directly accessing or refreshing non-root pages, browsers return "Cannot GET /path" errors, while in-app navigation works correctly. This article examines the root cause from technical principles and provides cross-server environment solutions.
Problem Manifestation and Root Cause Analysis
Consider a typical AngularJS application configuration example:
app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix = '!';
$routeProvider.when('/', {
templateUrl: '/views/index.html',
controller: 'indexCtrl'
});
$routeProvider.when('/about', {
templateUrl: '/views/about.html',
controller: 'AboutCtrl'
});
}]);After enabling html5Mode(true), in-app navigation using ng-href or $location.path() displays URLs as localhost:9000/about instead of localhost:9000/#/about. When users first visit /about or refresh that page, browsers send GET requests to localhost:9000/about. Since no corresponding /about resource exists on the server, this results in 404 errors.
In contrast, when users navigate from the root path / to /about via Angular routing, Angular intercepts the request client-side, directly loading the /views/about.html template without server requests to /about. This explains why in-app navigation succeeds while direct access fails.
Core Solution: Server-Side URL Rewriting
According to Angular's official documentation, HTML5 mode requires server-side URL rewriting to redirect all requests to the application's entry point (typically index.html). This is because Angular applications are single-page applications (SPAs) where all routing logic is handled client-side by JavaScript; the server only needs to serve the entry file, with subsequent routing managed by Angular.
Below are specific implementation methods for different server environments:
Apache Server Configuration
In Apache servers, URL rewriting can be achieved via a .htaccess file. Place the following rules in a .htaccess file at the application root:
RewriteEngine On
Options FollowSymLinks
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.html [L]This configuration redirects requests to /index.html if the requested file (-f) or directory (-d) does not exist. Thus, when browsers request /about, Apache returns index.html, and Angular loads the appropriate view based on the current URL.
Node.js/Express Server Configuration
In Node.js environments using Express, similar functionality can be implemented via middleware. Add a catch-all route handler after other route definitions:
app.use(function(req, res) {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});This middleware catches all requests not handled by preceding routes and returns index.html. Ensure it is placed after API routes to avoid interfering with normal API requests.
BrowserSync and Gulp Development Environment
When using BrowserSync and Gulp in development environments, the connect-history-api-fallback middleware can resolve this issue. First install the package:
npm install --save-dev connect-history-api-fallbackThen configure BrowserSync in the Gulp task:
var historyApiFallback = require('connect-history-api-fallback');
gulp.task('serve', function() {
browserSync.init({
server: {
baseDir: "app",
middleware: [historyApiFallback()]
}
});
});This middleware automatically redirects 404 requests to index.html, simulating production environment URL rewriting behavior.
Additional Configuration Considerations
Beyond server-side rewriting, note these configuration details:
Setting a <base> tag in HTML can specify the application's base URL, but Angular 1.2+ no longer recommends explicitly declaring <base href="/index.html"> as the $location service handles this automatically. Ensure all internal links use relative paths or Angular's routing methods.
For hash fallback mode (with hashPrefix), server-side rewriting is equally necessary, but URLs will include the #!/about format. HTML5 mode aims to eliminate this format, providing completely clean URLs.
Conclusion
The key to solving page reload issues in AngularJS HTML5 mode lies in understanding the collaboration between client-side routing and server-side serving. By configuring servers to redirect all non-static resource requests to index.html, Angular applications can correctly bootstrap and handle routing from any URL. Implementations vary slightly across server environments (Apache, Node.js, development servers), but the core principle remains consistent. Proper configuration enables support for direct deep linking, browser refreshes, and bookmarking, delivering a seamless user experience.