170

I am trying to pass parameters to a URL which looks like this:

http://www.foobar.com/foo?imageurl=

And I want to pass the parameters such as an image URL which is generated itself by another API, and the link for the image turns out as:

http://www.image.com/?username=unknown&password=unknown

However, when I try to use the URL:

http://www.foobar.com/foo?imageurl=http://www.image.com/?username=unknown&password=unknown

It doesn't work.

I have also tried using encodeURI() and encodeURIComponent() on the imageURL, and that too doesn't work.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Apoorv Saxena
  • 4,086
  • 10
  • 30
  • 46
  • What language is generating the URL? JavaScript? – Phil Nov 15 '11 at 10:53
  • 7
    Note that you should not put passwords in urls, not even when using https, as every router between client and server will see the whole url. – fabb Aug 30 '18 at 05:54
  • Does this answer your question? [How to create query parameters in Javascript?](https://stackoverflow.com/questions/111529/how-to-create-query-parameters-in-javascript) – Nick Grealy Aug 25 '20 at 03:16
  • 1
    @fabb That doesn't seem to be true: https://stackoverflow.com/a/499594/513038 – Erhannis Dec 08 '20 at 17:01
  • @Erhannis GET path and params are encrypted for HTTPS, but not HTTP (which, ok, no-one should be using, but still...). However, much more seriously, server logs and any analytics tools will see the GET params, so you'll log plaintext passwords in your apache logs, and in your Google Analytics. They're also sent through the referrer header, if that's enabled. See https://security.stackexchange.com/questions/233795/are-url-parameters-of-get-and-post-requests-over-https-secure – Dan W Sep 03 '21 at 10:39

4 Answers4

217

With PHP

echo urlencode("http://www.image.com/?username=unknown&password=unknown");

Result

http%3A%2F%2Fwww.image.com%2F%3Fusername%3Dunknown%26password%3Dunknown

With Javascript:

var myUrl = "http://www.image.com/?username=unknown&password=unknown";
var encodedURL= "http://www.foobar.com/foo?imageurl=" + encodeURIComponent(myUrl);

DEMO: http://jsfiddle.net/Lpv53/

Niels
  • 48,601
  • 4
  • 62
  • 81
63

Using new ES6 Object.entries(), it makes for a fun little nested map/join:

const encodeGetParams = p => 
  Object.entries(p).map(kv => kv.map(encodeURIComponent).join("=")).join("&");

const params = {
  user: "María Rodríguez",
  awesome: true,
  awesomeness: 64,
  "ZOMG+&=*(": "*^%*GMOZ"
};

console.log("https://example.com/endpoint?" + encodeGetParams(params))
Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110
Kyle VanderBeek
  • 1,047
  • 9
  • 7
  • This works very well in my case I added a call to decodeURIComponent inside the map function to make sure entries already encoded dont get messi – Herbert Pimentel Dec 23 '21 at 13:34
26

With URLSearchParams:

const params = new URLSearchParams()
params.set('imageurl', 'http://www.image.com/?username=unknown&password=unknown')
return `http://www.foobar.com/foo?${params.toString()}`
serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • Will this work in all browsers and browser versions? – PlsWork Apr 25 '22 at 11:37
  • @PlsWork : while it's a standard, some <1%-browsers have not implemented it: https://caniuse.com/urlsearchparams – serv-inc Apr 25 '22 at 13:24
  • 1
    Why not use the entirety of the [`URL` API](//developer.mozilla.org/en/docs/Web/API/URL) and start with `const url = new URL("http://www.foobar.com/foo");`? Also, in most circumstances you want [`set`](//developer.mozilla.org/en/docs/Web/API/URLSearchParams/set), not [`append`](//developer.mozilla.org/en/docs/Web/API/URLSearchParams/append): `url.set("imageurl", "http://www.image.com/?username=unknown&password=unknown"); return url;`. – Sebastian Simon Dec 02 '22 at 14:02
  • @SebastianSimon: good point about set vs append, thanks. Using URL might distract from the main point, since setting the params does not seem to be elegant. Or did I overlook something? – serv-inc Dec 12 '22 at 09:50
  • 1
    @serv-inc Depends on the use case; using `URL` itself was just a general suggestion. I prefer to have my URLs always validated by the URL API, even if they’re static, so it might be a bit of an overkill; it’s just preference. – Sebastian Simon Dec 12 '22 at 11:31
10

Just try encodeURI() and encodeURIComponent() yourself...

console.log(encodeURIComponent('@#$%^&*'));

Input: @#$%^&*. Output: %40%23%24%25%5E%26*. So, wait, what happened to *? Why wasn't this converted? TLDR: You actually want fixedEncodeURIComponent() and fixedEncodeURI(). Long-story...

You should not be using encodeURIComponent() or encodeURI(). You should use fixedEncodeURIComponent() and fixedEncodeURI(), according to the MDN Documentation.

Regarding encodeURI()...

If one wishes to follow the more recent RFC3986 for URLs, which makes square brackets reserved (for IPv6) and thus not encoded when forming something which could be part of a URL (such as a host), the following code snippet may help:

function fixedEncodeURI(str) { return encodeURI(str).replace(/%5B/g, '[').replace(/%5D/g, ']'); }

Regarding encodeURIComponent()...

To be more stringent in adhering to RFC 3986 (which reserves !, ', (, ), and *), even though these characters have no formalized URI delimiting uses, the following can be safely used:

function fixedEncodeURIComponent(str) { return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { return '%' + c.charCodeAt(0).toString(16); }); }

So, what is the difference? fixedEncodeURI() and fixedEncodeURIComponent() convert the same set of values, but fixedEncodeURIComponent() also converts this set: +@?=:*#;,$&. This set is used in GET parameters (&, +, etc.), anchor tags (#), wildcard tags (*), email/username parts (@), etc..

For example -- If you use encodeURI(), user@example.com/?email=me@home will not properly send the second @ to the server, except for your browser handling the compatibility (as Chrome naturally does often).

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
  • What do you mean by `"except for your browser handling the compatibility"`? – Stephan Jul 31 '20 at 06:12
  • 1
    @Stephan: For example, if `site.com?formula=a+b=c` works in producing `formula=>a+b=c`, that violates the spec, but if it works, it's because Chrome/etc. can detect the spec failure of the URL, etc., and so on, w/ `user@site.com?email=user@site.com`. – HoldOffHunger Jul 31 '20 at 14:42