50

I have my react app running great on my local dev server but it did not work when I dump my production ready files straight into Apache's htdocs directory:

Here is what I have:

/var/www/index.html
/var/www/bundle.js

and I have

DocumentRoot /var/www

in /etc/apache2/sites-available/000-default.conf

The fact is that
1). when I access http://...com/ that routed me to Login page
2). After I clicked a link

<Link to="main"><button>Log In</button></Link>

the content in the browser location field become:

http://...com/main

3). Now if I reload this url (http://...com/main), I got

The requested URL /main was not found on this server

My rounting in React:

    <Router history={browserHistory }>
      <Route path="/" component={TopContainer}>
          <IndexRoute component={Login} />
          <Route path='main' component={MainContainer} />   
      </Route>
</Router>

What else I am missing in the apache configuration?

thanks

Mark Qian
  • 753
  • 2
  • 10
  • 16

10 Answers10

75

Change the VirtualHost configuration (typically found in /etc/httpd/conf.d\vhosts.conf) by adding the following Rewrite* lines:

<VirtualHost *:8080>
  ServerName example.com
  DocumentRoot /var/www/httpd/example.com

  <Directory "/var/www/httpd/example.com">
    ...

    RewriteEngine On
    # Don't rewrite files or directories
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    # Rewrite everything else to index.html to allow html5 state links
    RewriteRule ^ index.html [L]
  </Directory>
</VirtualHost>

This tells Apache to serve any files that exist, but if they don't exist, just serve /index.html rather than a 404: not found.

Complete answer gratefully stolen from here

Edit: 'On' need to be uppercase in current apache version

Tino Rüb
  • 799
  • 2
  • 13
  • 27
Hinrich
  • 13,485
  • 7
  • 43
  • 66
  • 5
    To explain further what this is doing, if the `RewriteCond` is matched (i.e. the file or directory exists) then the the `RewriteRule ^ - [L]` indicates "do nothing". Otherwise, it will redirect any other traffic to index.html. – kojow7 May 08 '19 at 21:42
  • How would this rule be if I want to rewrite directories only but have 404 on files? – Chris Sep 05 '19 at 08:58
  • 1
    Thank you @Hinrich, this solution worked for me in ubuntu 20.04 for react 16.13.0 and react-router-dom 5.1.2. – Manikandan S May 29 '20 at 19:39
  • For me it only worked when nested inside a Directory block like above – TeT Psy May 17 '23 at 08:00
30

The above solution works for Ubuntu as well but I have struggled a bit with it so here are the steps necessary to make it work.

Location of the file where you need to place the above mentioned configuration is under

/etc/apache2/sites-enabled

default is

/etc/apache2/sites-enabled/000-default.conf

Then you need to make sure that RewriteEngine is running (otherwise you will get an error when restarting Apache server).

sudo a2enmod rewrite

Finally, restart Apache server

sudo /etc/init.d/apache2 restart

Now, it should work.

When you are using default configuration (root of the website is under /var/www/html), then all you need to do is to place

<Directory "/var/www/html">
    RewriteEngine on
    # Don't rewrite files or directories
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    # Rewrite everything else to index.html to allow html5 state links
    RewriteRule ^ index.html [L]
</Directory>

to the above mentioned file under <VirtualHost ...>

Matus Dubrava
  • 13,637
  • 2
  • 38
  • 54
18

If you have to use .htaccess and a sub directory then following works for me.

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
Ali Nouman
  • 3,304
  • 9
  • 32
  • 53
15

What worked for me, echoing many of the answers and comments here:

  1. sudo a2enmod rewrite
  2. Open up /etc/apache2/apache2.conf
  3. Paste in this with the path to your root:
<Directory "/var/www/PATH_TO_YOUR_ROOT">
    RewriteEngine on
    # Don't rewrite files or directories
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    # Rewrite everything else to index.html to allow html5 state links
    RewriteRule ^ index.html [L]
</Directory>
  1. sudo service apache2 restart

Pasting into the site-specific conf file did not work as earlier answers suggested.

lawrence-witt
  • 8,094
  • 3
  • 13
  • 32
5

None of the solutions posted so far appear to address the issue where missing ressources incorrectly return 200 instead of 404, which can make debugging when certain files are missing rather annoying.

My solution is to instead watch what type of resource the request expects to recieve, since browsers will ask for HTML when navigating to a page (Firefox asks for text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8) but not when accessing resources after the initial load (JS files imported via <script> or as ES6 modules ask for */*, CSS files ask for text/css,*/*;q=0.1, accessing JSON via the fetch() API will specify application/json, text/plain, */* and so on). By relying on that assumption, one can configure Apache to serve the Single page app when trying to access a non-existent file (such as a route that only works within the Single-page app) without also sending it whenever said SPA asks for a CSS file that has been renamed or a missing JSON file.

EDIT: MDN has a list of common values for the Accept header.

<Directory  "/var/www/httpd/example.com">
    RewriteEngine on

    # Browsers will specifically ask for HTML (among other things) on initial page load
    # That is, if the *user* tries to access a *nonexisting* URL, the app is loaded instead
    # but if a webpage attempts to load a missing resource it will return 404.
    # (You can still go to /myreactapp/favicon.ico, but a missing /myreactapp/favicon.png resource won't return 200)

    # if (HTTP_ACCESS.contains('text/html') && file_not_exists(REQUEST_FILENAME))
    RewriteCond %{HTTP_ACCEPT} text/html
    RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^ index.html [last]

    # Any ressources loaded by index.html should behave correctly (i.e: Return 404 if missing)
    RewriteRule ^ - [last]

    AllowOverride None
    Options FollowSymLinks Multiviews 
    Require all granted
</Directory>
VLRoyrenn
  • 596
  • 5
  • 11
  • I combined your solution with @MatusDubrava's answer and it worked for me on Ubuntu. – BBaysinger Nov 25 '19 at 00:15
  • Added the 5 first lines (RewriteEngine, RewriteConds and RewriteRules) into a .htaccess on the html_public folder of my app and it was enough. – Sdlion Mar 20 '21 at 01:49
3

Thank you! This worked for me.

I am pasting my config if you are serving multiple sites (virtualhost) and also SSL certificates (SSL was made with certbot), with redirect http to https

This setting works on Linode / Ubuntu

yoursite.com-le-ssl.conf

<IfModule mod_ssl.c>
<VirtualHost *:443>
  # Admin email, Server Name (domain name), and any aliases
  ServerAdmin webmaster@yoursite.com
  ServerName  yoursite.com
  ServerAlias www.yoursite.com

  # Index file and Document Root (where the public files are located)
 DirectoryIndex index.html index.php
  DocumentRoot /var/www/html/yoursite.com/public_html 
<Directory  "/var/www/html/yoursite.com/public_html">
    RewriteEngine on

    # Browsers will specifically ask for HTML (among other things) on initial page load
    # That is, if the *user* tries to access a *nonexisting* URL, the app is loaded instead
    # but if a webpage attempts to load a missing resource it will return 404.
    # (You can still go to /myreactapp/favicon.ico, but a missing /myreactapp/favicon.png resource won't return 200)

    # if (HTTP_ACCESS.contains('text/html') && file_not_exists(REQUEST_FILENAME))
    RewriteCond %{HTTP_ACCEPT} text/html
    RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^ index.html [last]

    # Any ressources loaded by index.html should behave correctly (i.e: Return 404 if missing)
    RewriteRule ^ - [last]

    AllowOverride None
    Options FollowSymLinks Multiviews 
    Require all granted
</Directory>
  # Log file locations
  LogLevel warn
  ErrorLog  /var/www/html/yoursite.com/log/error.log
  CustomLog /var/www/html/yoursite.com/log/access.log combined
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/yoursite.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yoursite.com/privkey.pem
</VirtualHost>
</IfModule>
1

This is what we use at work for our production react app which is using BrowserRouter from react-router:

httpd.conf

<VirtualHost *:3000>
    DocumentRoot "/var/www/html"

    <Directory /var/www/html/>
        Header set Cache-Control "no-cache"
        # https://stackoverflow.com/a/34154531/2089675
        FallbackResource /index.html
    </Directory>

    <Directory /var/www/html/static/>
        # https://create-react-app.dev/docs/production-build/#static-file-caching
        Header set Cache-Control "public, max-age=31536000"
        # https://stackoverflow.com/a/54943214/5600537
        RequestHeader edit "If-None-Match" '^"((.*)-gzip)"$' '"$1", "$2"'
    </Directory>
</VirtualHost>

As you can see most of the comments in there are answers from SO, so I'm just giving back :)


configuration

Place the above file in /usr/local/apache2/conf/httpd.conf.

The config also assumes that you have put the contents of the build folder inside /var/www/html/. If you've placed them elsewhere, then adjust the path accordingly.

ports

The VirtualHost *:3000 part is just for exposing the server's port in the docker container (httpd:buster) used to run it. This is also the same port CRA defaults to in dev. An external proxy is used to manage where the application can be accessed from.

compression

Finally, if you are interested in serving gzipped files you may want to remove the RequestHeader edit line, and then do some more work to make sure .gz files can be served:

ex.

AddOutputFilterByType DEFLATE text/html application/javascript
smac89
  • 39,374
  • 15
  • 132
  • 179
1

Solution:

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

If you've multiple virtual host then follow these steps

  • Goto to that VH and open the .htaccess file
  • add these lines and save it
  • restart the apache service again so that it can reflect into the settings
0

React routing issue fixed on ubantu server

Solution:

  1. Open the file using the console.

If you are using SSL nano /etc/apache2/sites-available/000-default-le-ssl.conf

Add the following lines

===================================================================================

DocumentRoot /var/www/project <Directory "/var/www/project"> RewriteEngine on RewriteCond %{HTTP_ACCEPT} text/html RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.html [last] RewriteRule ^ - [last]

AllowOverride None
Options FollowSymLinks Multiviews 
Require all granted
-3

Go on this directory

  1. /etc/apache2/sites-available

  2. open File : 000-default.conf

  3. Change its permission : 777

  4. Paste code on bottom of file

RewriteEngine on 

# Don't rewrite files or directories 

RewriteCond %{REQUEST_FILENAME} -f [OR] 

RewriteCond %{REQUEST_FILENAME} -d 

RewriteRule ^ - [L] 

# Rewrite everything else to index.html to allow html5 state links 

RewriteRule ^ index.html [L] 

  1. Restart server