Search code examples
.htaccesswebroot

Change web root directory - problem with anchor links


I tried edit web root directory via .htaccess file:

RewriteCond %{REQUEST_URI} !newweb/
RewriteRule (.*) /newweb/ [L]
RewriteCond %{REQUEST_FILENAME} !-f

and in index.html code change CSS, JS links via base tag

<base href="http://example.com/newweb/">

but anchror links not redirect to example.com/#tag but to example.com/newweb#tag

I need the first option to work.


Solution

  • ...but anchror links not redirect to example.com/#tag but to example.com/newweb#tag

    Yes, using a base element (to correct relative URLs) is not without its caveats. An in-page anchor such as #tag is a "relative URL", so this is now relative to the base URL.

    The only way to resolve this is to fully qualify your in-page links, with a root-relative (or absolute) URL to the current page/URL. (Which may defeat the point of using the base tag in the first place as a workaround for your other relative URLs.) Or don't use relative URLs to your resources / other pages in the first place.

    See my answer to a similar question on the Pro Webmasters stack for more detail:


    UPDATE: Having had a look at your website, which is essentially a one-page website that relies on fragment identifiers (ie. #tag) for all in-page links then you should change all relative URLs to your resources (CSS, JS and images) to be root-relative (starting with a slash) and remove the base element.

    By "root-relative", it should be root-relative to your expected client-side root, not the actual filesystem root.

    For example, the following relative URL in your HTML source:

    vendor/bootstrap/css/bootstrap.min.css
    

    Should be changed to a root-relative URL (not a filesystem path):

    /vendor/bootstrap/css/bootstrap.min.css
    

    Note that this does not include the /newweb filesystem subdirectory.

    The directives in .htaccess then internally rewrite the URL to /newweb/vendor/bootstrap/css/bootstrap.min.css (the underlying filesystem path). The /newweb subdirectory is then entirely hidden from the client.

    By using the base tag in your HTML you are also exposing the /newweb subdirectory to the client (and search engines) and potentially creating duplicate content, since your site is available at both example.com/ and example.com/newweb/.

    RewriteCond %{REQUEST_URI} !newweb/
    RewriteRule (.*) /newweb/ [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    

    However, these directives are not correct. Do you have other directives in your .htaccess file? The last RewriteCond directive is invalid on its own at the end. And the preceding RewriteRule rewrites everyithing to the directory root (which may explain why you resorted to using relative URLs in your HTML source?)

    To rewrite everything to the corresponding URL in the /newweb subdirectory then you would need something like:

    RewriteEngine On
    
    # Rewrite enverything to the /newweb subdirectory
    RewriteRule !^newweb/ /newweb%{REQUEST_URI} [L]
    

    This assumes that your page only references resources that are contained within the /newweb subdirectory and not elsewhere on the filesystem. If you are then you will also need a filesystem check.

    However, by itself, this doesn't resolve the issue of example.com/newweb/ still being accessible (although this is less important now that the URL-path is not exposed). By this can be resolved with an external redirect from /newweb back to the document root (but we need to be careful of a redirect loop, given the rewrite in the opposite direction).

    So, try the following instead:

    RewriteEngine On
    
    # If /newweb is requested directly then redirect to remove it
    RewriteCond %{REDIRECT_STATUS} ^$
    RewriteRule ^newweb/?(.*) /$1 [R=302,L]
    
    # Rewrite enverything to the /newweb subdirectory
    RewriteRule !^newweb/ /newweb%{REQUEST_URI} [L]
    

    Change the 302 (temporary) rediredct to 301 (permanent) only when you have confirmed it is working OK.