qq interpolates no matter what opening and closing characters you use, so characters such as $ and \ will have special meaning in the string. If you don't want anything interpolated into the string, use a heredoc with single quotes:
print <<'END_HTML';
<!DOCTYPE html>
<html>
...
</html>
END_HTML
However, generating HTML like this is pretty error-prone, as you've discovered. I suggest you look into a templating system such as Template::Toolkit.
As for debugging CGI scripts, you should check your server's logs for the cause of a 500 error. See also CGI Help Guide, Troubleshooting Perl CGI scripts, and your browser's debugging tools to inspect the HTML.
Update: The reason you're seeing a difference between qq|| and qq{}/qq() is that the latter supports nested brackets, so e.g. print qq{a{b}c}; prints a{b}c, whereas qq|a|b| is not valid (hence the 500 error), as the second | terminates the string - you'd have to write qq|a\|b| to get the string "a|b". |