4

I have a website where users can upload files, if they upload a file with a special character in the filename, the filename will be encoded (for example: iordănescu.pdf will become iord%C4%83nescu.pdf)

This causes some problems to be able to open these files from a link on the website. The CMS provides a link like this one: example.com/frontend/files/jobs/iord%C4%83nescu.pdf.

But it only works when the encoded filename is encoded (so double encoded) like this: example.com/frontend/files/jobs/iord%25C4%2583nescu.pdf.

Can I write a rule in my .htaccess file to make the filenames double encoded? Or is there something else I can try?

I've updated my current .htaccess file, which is saved in the same folder where the files are uploaded to, to the suggestion of MrWhite:

Options +FollowSymLinks -MultiViews

RewriteEngine On

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{THE_REQUEST} "^GET /(.*%.*) HTTP/"
RewriteRule \.(pdf|jpg|png)$ /%1 [B,L]

This now gives me the following error:

The requested URL /frontend/files/jobs/iord%C4%83nescu.pdf was not found on this server.

Which is how I see the file via FTP, but if I copy the path via FTP it appears to be double encoded already:

/www/frontend/files/jobs/iord%25C4%2583nescu.pdf

Side note:

This website has been developed by someone else and is rather old. I am now responsible for maintaining this website and the client informed me about this issue. Since this website will be completely redone soon I don't want to spend too much time on creating a fix so being able to solve it via .htaccess would be ideal.

2
  • 1
    What exactly is the URL being requested? And where is the .htaccess file? The requested URL stated does not appear to match the directive in the .htaccess file you posted? And you state the .htaccess file is in the "folder where the files are uploaded", yet you are still referring to a long URL path in the directive? The directive also references a "language code" that is not present in the URL stated?
    – MrWhite
    Sep 27, 2019 at 16:40
  • Is this your own server-side code / CMS? Do you have access to "fix" the code that saves the file and/or generates the link.
    – MrWhite
    Sep 27, 2019 at 16:43

1 Answer 1

4

Ideally, this should be "fixed" in the backend code, as there appears to be two "bugs" here...

  1. The uploaded file should not be saved with a %-encoded filename. This is a URL-encoding, intended for transit, not a filesystem encoding.

  2. The generated link is not being correctly URL encoded. If the underlying filename is "URL encoded" then the generated link should already be "doubly" encoded.

However, this can be "worked around" in .htaccess with a URL-rewrite. I would avoid a Redirect. If you redirect then you are doubling the requests, putting extra load on the server, slowing the user and depending on the user-agent for part of the URL-encoding process.

You also need to be as specific as possible with the URLs you target as you need to avoid doubly encoding any legitimate URL that shouldn't be!

You need to clarify the URL and filesystem paths, as this is currently not clear in your question - your example URLs and .htaccess directives appear to target different things?

For the sake of this example, I will assume the user can only upload .pdf, .jpg and .png files.

In the .htaccess file in the directory where the files are uploaded, try something like the following using mod_rewrite:

Options +FollowSymLinks -MultiViews

RewriteEngine On

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{THE_REQUEST} "^GET /(.*%.*) HTTP/"
RewriteRule \.(pdf|jpg|png)$ /%1 [B,L]

The RewriteRule pattern matches the location and type of files that can be uploaded. We can't simply capture the URL-path using the RewriteRule pattern (as you are doing in your example), because the URL-path that the RewriteRule pattern matches against is already %-decoded. Instead, we grab the (hopefully) single-encoded-URL from THE_REQUEST server variable. THE_REQUEST contains the first line of the HTTP request and contains the (encoded) root-relative URL as sent from the client. We check that the request contains at least one % - so we know it is %-encoded.

The %1 backreference (note the % prefix, not $) references the captured subpattern in the last matched condition (ie. the encoded URL). The B flag ensures that this backreference is URL encoded (again).

The first condition that checks against the REDIRECT_STATUS environment variable ensures that we only examine the initial request from the client and not the rewritten request by our directive. This avoids an endless rewrite loop. We only need to loop once.

1
  • Thanks, I've updated my question with the outcome of your suggestion and also answered your questions.
    – Alessandro
    Sep 30, 2019 at 7:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.