Content negotiation happens between the browser and the web server. It helps the server to know what content to send back.
It all works via different HTTP headers and can quickly become complex, but here is the gist of it:
1. Accept-Charset: Let the client set the charset (utf8, etc.)
But isn't used in practice as all browsers supports most charsets and will just render the page based on the response.
2. Accept-Encoding: Simply telling what compression method the browser supports. The usual list on Chrome is gzip, deflate, sdch, br. You notice br here, being Brotli, the latest compression algo from Google.
3. Accept-Language: Obviously about the language preference of the browser.
Less obvious, the ;q=0.6 syntax, q here stands for quality, just a weight to tell the server the most/least preferred languages.
4. Accept: This could have been named Accept-Mime since it’s all about the file type.
It also accepts the quality weight and usually looks like text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 for HTML page requests.
Now, the web server can also tell about what content it can serve via the Vary response header.
A typical usecase is Vary: User-Agent, meaning different User-Agent (think mobile/desktop) will get different content.