Proper .htaccess settings for Next.js SSG catch all routes - .htaccess

I've build a website, using Next.js and SSG - Static Site Generation.
I'm serving the website on an Apache server and, therefore, because of this answer, I'm using the following .htaccess configuration:
# Disable directory indexes and MultiViews
Options -Indexes -MultiViews
# Prevent mod_dir appending a slash to directory requests
DirectorySlash Off
# Rewrite /foo to /foo.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.html [L]
# Otherwise, rewrite /foo to /foo/index.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/index.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}/index.html [L]
# Handling 404
ErrorDocument 404 /my-custom-404.html
And it's been working perfectly.
But recently, there was a need for an admin page, the usual CRUD. I've developed it without SSG in mind (because there were actually no need for it), created my own custom router and used Next.js's catch all routes to handle all content of this admin page, which I called dashboard.
So, on my built website, I have a whole bunch of page.html files, and a /dashboard/[[...dashboard]].html file, which is the CRUD itself (all subpages and contents are imported dynamically) - and, therefore, every single link starting with /dashboard will be handled via that file.
So, the problem is: navigating through the links on the website and into the dashboard, everything works perfectly. But if I try to directly access any link within /dashboard (including itself, and subroutes: /dashboard/users, for instance), it simply does not work. With the provided .htaccess configuration, it goes straight to 404.
I've tried a few different configurations added to .htaccess, but none have worked so far (either it still goes straight to 404, or the browser shows the error ERR_TOO_MANY_REDIRECTS):
RewriteCond %{DOCUMENT_ROOT}/dashboard/$1
RewriteRule (.*) /dashboard/[[...dashboard]].html [L]
This one almost worked... but the dashboard itself does not do anything (no subpages are ever rendered):
RewriteCond %{REQUEST_URI} .*dashboard*.
RewriteRule (.*) /dashboard/[[...dashboard]].html [L]
Important note: this whole thing works perfectly on the local dev server.
If anyone has any tip on how I can sort this thing out, I would really appreciate it!

I've managed to solve it, at least on an acceptable level.
As I mentioned on the question, this was almost working (no sub page was ever rendered, but at least the browser was correctly redirecting to the dashboard):
RewriteCond %{REQUEST_URI} .*dashboard*.
RewriteRule (.*) /dashboard/[[...dashboard]].html [L]
No page was ever rendered, because I was using Next.js's withRouter/useRouter to inform my custom router of the current route (via router.asPath), and when the above rule as met, router.asPath was set to dashboard/[[...dashboard]].html.
So, I wrote a small test to redirect any access with that asPath to my entrypoint. Basically:
if(router.asPath.contains('[[...dashboard]]')
router.push('/dashboard')
But, another problem arouse...
The first problem was solved: trying to access any dashboard routes directly was working, but navigating through the website (links) was not. After an arduous and long process of trial and error, I found out that my previous .htaccess rule was preventing my client-side code to request my [[...dashboard]].html file.
RewriteCond %{REQUEST_URI} .*dashboard*.
RewriteCond %{REQUEST_URI} !(\[\[\.\.\.dashboard\]\])
RewriteRule (.*) /dashboard/[[...dashboard]].html?p=$1 [L]
Basically:
If my request URI contains 'dashboard'
AND if that same request does not contain [[...dashboard]]
THEN I rewrite it to the dashboard file
Hope this might help someone someday!

Related

htaccess rewrite stopped working properly

I have the following code block in my .htaccess file:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^(.*)2021layout(.*)$
[other directories to omit, like assets and admin]
RewriteRule ^(.*) RESThandler.php
Basically I want certain directories to be processed normally, like 2021layout, while others use the REST handler. In the 2021layout directory, the member area is in the directory 2021layout/myaccount, and all css/js files are in 2021layout/assets/[whatever directory]. Both the assets and myaccount directories have permissions 0755.
On Friday, everything was working fine. Today, having changed nothing, pages in the 2021layout directory are working, and css and js files loaded by those pages are fine, but pages in the 2021layout/myaccount directory are trying to use the REST handler and getting redirected to my 404 page.
I have tried renaming both the 2021layout and myaccount directories, which didn't work. I have tried adding !^(.*)2021layout/myaccount(.*)$ as a RewriteCond, and that didn't work.
Why would this one specific directory suddenly stop obeying my htaccess instructions? Can I fix this?
Again, to reiterate: I didn't change anything to make this happen. It worked one day, and the next day it didn't, seemingly on its own.
EDIT: I have gotten the directory to work again by renaming the newly-created file settings.php to mysettings.php. So apparently the mere existence of settings.php within the directory was preventing it from loading correctly. Does anyone have any insight into this?
You may try this rule with THE_REQUEST:
RewriteCond %{THE_REQUEST} !\s/2021layout/ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ RESThandler.php [L]
THE_REQUEST variable represents original request received by Apache from your browser and it doesn't get overwritten after execution of other rewrite directives. Example value of this variable is GET /index.php?id=123 HTTP/1.1
Make sure to clear your browser cache before testing this change.
So apparently the mere existence of settings.php within the directory was preventing it from loading correctly. Does anyone have any insight into this
I am guessing that you have option MultiViews turned on in your Apache config. To turn it off use this directive at top of your .htaccess:
Option -MultiViews
Option MultiViews (see http://httpd.apache.org/docs/2.4/content-negotiation.html) is used by Apache's content negotiation module that runs before mod_rewrite and makes Apache server match extensions of files. So if /file is the URL then Apache will serve /file.html.

.htaccess root url to subfolder, and specific url to root folder

My setup to weird, needless to say im particularly limited in terms of what i can change. At the moment, the easiest thing for me to change is the server's .htaccess file. The current directory setup looks like:
/ is a wordpress installation
/front-end is a react website that uses the wordpress installations REST API to get data
I am trying to get it so that when i go to example.com it serves content from /front-end but all requests to /dashboard are served from the root directory.
For example these URLs would do the following:
/ and /contact would point to the index.html file inside the front-end directory (react-router would pick up the /contact)
/dashboard/wp-admin and /dashboard/wp-json/menus would let wordpress in the route directory take over. (these would ALWAYS start /dashboard)
Is what im trying to achieve doable?
I have this to start with, but im unsure how to ignore the /dashboard route
RewriteBase /
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-s
RewriteCond %{DOCUMENT_ROOT}/front-end/%{REQUEST_URI} -s
RewriteRule . /front-end/index.html [L]
RewriteEngine On
RewriteRule ^/?dashboard/(.*)$ /$1?wordpress=true [L,NC]
RewriteCond %{QUERY_STRING} !^.*wordpress=true.*$ [NC]
RewriteCond %{REQUEST_URI} !^/?front\-end.*$ [NC]
RewriteRule ^(.*)$ front-end/$1 [L,NC]
In the above rules, we first check if we have dashboard in our requests. If yes, we deliberately attach a query string wordpress=true to avoid confusion with your react folder and run into a too many redirects issue.
This way, we also know that the current request is for wordpress and further rewrite rules should not touch it.
In the second rewrite rule, we check the conditions as to whether we have wordpress=true or not and see if this does not already have front-end in the URL(also applicable for internal routing and need not be explicitly specified). If it passed both tests, we redirect it to your react front-end, else we leave it to execute as is.
Reason to attach !^/?front\-end.*$ is to avoid infinite internal redirects to itself.

Trying to put website into Maintenance Mode (302) - 'Too many redirects' htaccess issue

I'm trying to put my webpage into Maintenance Mode by using htaccess to redirect any page that begins with (domain name) to a maintenance.php file within a folder inside the root.
I got this to work on localhost with no issues, but it just won't work when I put it on my web host server. It keeps saying there are too many redirects (there's an infinite loop going on).
# MAINTENANCE-PAGE REDIRECT
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REMOTE_ADDR} !^100\.184\.54\.96
RewriteCond %{REQUEST_URI} !/maintenance/maintenance.php$ [NC]
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif) [NC]
RewriteRule .* /maintenance/maintenance.php [R=302,L]
</IfModule>
I tried plenty of the answers given to other questions such as
.htaccess error - ERR_TOO_MANY_REDIRECTS
htaccess maintenance page redirect results in "too many redirects" error
...among others. The same error keeps coming. I have another domain (domain-1) redirecting to the current webpage (domain-2), tried turning that off to see if it works, nope.
After following a ton of suggestions and styles from around the net, I finally came to a solution that worked for this issue.
To redirect all pages and sub-directories for your domain name to a maintenance page, create two files:
maintenance.html (maintenance page)
maintenance.enable (empty file)
Use the following code in your .htaccess file:
RewriteEngine On
RewriteCond %{REMOTE_ADDR} !^105\.228\.123\.16
RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
RewriteCond %{DOCUMENT_ROOT}/maintenance.enable -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /maintenance.html [R=503,L]
ErrorDocument 503 /maintenance.html
Header Set Cache-Control "max-age=0, no-store"
Be sure to place the 2 files in the same directory as your index page.
That's the solution that worked in my case. I'm yet to try it out with external resources (css/js files and images) but I think it shouldn't take more than some tweaking the above code. Hope it helps someone else too.
EDIT
For external resources and styling just add this line:
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif|css|js|ico)
Be sure to add all of the relevant directories (containing the stylesheets and scripts) in the same directory as the maintenance.html page.
I could be wrong but it seems like a bad idea to use this in conjunction with Header Set Cache-Control "max-age=0, no-store" if you're going to keep the maintenance page up for a while. I leave that for the experts though :-)
My maintenance page is a fancy countdown page.
This is actually part of the problem. Your "fancy" page contains links to numerous CSS and JS files (and the favicon.ico file) - 17 files in total - your .htaccess redirect will redirect these requests as well (all to your maintenance.php page - which will trigger further redirects etc.). You'll need to make additional exceptions for these URL/file extensions. For example:
RewriteEngine on
RewriteCond %{REMOTE_ADDR} !^100\.184\.54\.96
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif|css|js|ico)$
RewriteRule !maintenance\.php$ /maintenance/maintenance.php [R=302,L]
The <IfModule mod_rewrite.c> wrapper is not required (you know your server).
The NC flag is not required unless you really do have mixed case extensions.
I realise this isn't a normal "site down for maintenance" type page, however, maintenance pages should ideally link to as few external resources as possible. To avoid issues like the above, but also you don't want to be in a situation where the maintenance page itself cannot be displayed because the site is down for maintenance!

.htaccess RewriteRule not redirecting from real folder

I'm still a bit fuzzy on the working of .htaccess, and I've looked around but I can't find anything to help this specific issue.
EDIT: I realize there are other questions that seem like they cover this issue, but I checked some and they didn't seem to offer any help I could understand, and I didn't want to hijack them with my own issues.
This is what I have:
Options +FollowSymLinks
#RewriteBase /
RewriteEngine on
RewriteRule /mp3/(.*) http://old.domain.com/mp3/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)$ /index.php?p=$1 [L]
As you can see from the last line, the string typed after the server name is actually a URL parameter and depending on that parameter, different content is pulled from the database and that page is displayed on the site.
The problem I'm having is that the client has a content page called "podcast", so they would go to site.com/podcast which should quietly redirect to site.com/index.php?=podcast and load the content for that page. Unfortunately, the client also has a real site.com/podcast/ folder on their server. Because of this, the rewrite is ignored and the browser attempts to load that folder. It either shows a file listing or a forbidden error if I disable the listing.
After some research (I'm still new to htaccess), I learned that the two lines prior disable the rewrite if the path points to an actual file or folder. Unfortunately, commenting out the one with !-d doesn't seem to have any effect, and commenting out both gives me a server error.
Admittedly, part of the problem here was lack of foresight. URL rewrites should have been planned before everything else was put together, but it wasn't until the site was basically completed that I was notified that the client wants "Friendly URLs" that don't include the ?p= part. Regardless, perhaps there is a way to fix this.
Is there some .htaccess trickery I can use that will force the rewrite even if the URL entered points to a folder (not a specific file) that actually exists? As stated before, removing the !-d doesn't seem to help, although I'm not sure why. Perhaps I misunderstand its purpose.
Thank you for any help, and please be lenient with me if I overlooked something obvious. This is an issue presenting itself on the client's live site right now so I feel a little rushed in solving it. Thanks again.
OH YEAH, and the solution can't be specific to /podcast. The way the client's site is set up, when they want to create a new subpage for the site, a new name is saved for that content based on their title for the page and it is possible (unlikely, but still possible) that another page can be created with a name that matches an existing folder on the server.
Here is a note from mod_rewrite documentation:
By default, mod_rewrite will ignore URLs that map to a directory on
disk but lack a trailing slash, in the expectation that the mod_dir
module will issue the client with a redirect to the canonical URL with
a trailing slash.
This explains why mod_rewrite ignores the URL /podcast. I would suggest that you rename physical directories so that do do not (accidentally) match article names.
Another option would be to disable the DirectorySlash setting. This will prevent Apache from redirecting /podcast to /podcast/.
DirectorySlash Off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]*)$ /index.php?p=$1 [L]
Be warned that disabling this setting has side effects. Read through the documentation first.
Change the following line of code:
RewriteRule ^([^/]*)$ /index.php?p=$1 [L]
to
RewriteRule ^(podcast([^?]*)) index.php?p=$1 [L,NC]

.htaccess rewrite rule for /

I have a website where if I go to the URL http://mysite.com/community it shows page not found. But, the URL http://mysite.com/community/ correctly displays the page. How can I set up a rewrite for that "/" after community?
This is my present .htaccess:
Options +FollowSymLinks
Options +Indexes
RewriteEngine On
RewriteRule ^admin$ Admin/index.php?qstr=$1 [L]
RewriteRule ^(.*)/$ index.php?qstr=$1 [L]
These were the ones tried by me, but failed
First,
RewriteRule ^(.*)/community $1/community/ [L]
second,
RewriteRule /community /community/ [L]
All with different combinations of with and without [L].
From the Apache URL Rewrite Guide:
Trailing Slash Problem
Description:
Every webmaster can sing a song about the problem of the trailing slash on URLs referencing directories. If they are missing, the server dumps an error, because if you say /~quux/foo instead of /~quux/foo/ then the server searches for a file named foo. And because this file is a directory it complains. Actually it tries to fix it itself in most of the cases, but sometimes this mechanism need to be emulated by you. For instance after you have done a lot of complicated URL rewritings to CGI scripts etc.
Solution:
The solution to this subtle problem is to let the server add the trailing slash automatically. To do this correctly we have to use an external redirect, so the browser correctly requests subsequent images etc. If we only did a internal rewrite, this would only work for the directory page, but would go wrong when any images are included into this page with relative URLs, because the browser would request an in-lined object. For instance, a request for image.gif in /~quux/foo/index.html would become /~quux/image.gif without the external redirect!
So, to do this trick we write:
RewriteEngine on
RewriteBase /~quux/
RewriteRule ^foo$ foo/ [R]
The crazy and lazy can even do the following in the top-level .htaccess file of their homedir. But notice that this creates some processing overhead.
RewriteEngine on
RewriteBase /~quux/
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+[^/])$ $1/ [R]
Well, after trying out all the above solutions as well as some of my own, I finally solved this. I'm definitely sure that this is NOT a complete solution but it sure solved it for the time being.
Solution: Just created an empty directory named "community" in the root folder. That's it!
But I'm still on the lookout for the actual solution to this.

Resources