This is an interesting question. There shouldn't be any reason for it to be a problem, but you should take a few things into account:
- The headers need to be unique to your application. Not just now, but forever. You should ensure you're prefixing them, e.g.
X-MyApplication-Foo: Bar. Not doing this might cause conflicts in future.
- Firewalls are sometimes (rarely) a little overzealous with filtering unknown HTTP headers. This shouldn't be a problem, but it's something to keep in mind.
- Older browsers have smaller limitations on header field sizes than modern browsers, so you need to test across as many as you can get hold of.
Is there are reason you can't use the standard HTTP error codes? I understand that you might want to provide stack traces or other useful debugging information, but in the case of an error, wouldn't you just return a JSON blob that contains the error information, rather than the normal "result" JSON data? You could easily detect the difference based on the HTTP error code and handle the two cases differently.
The reason I'm concerned by your suggested approach is that headers are used to alter or cause browser behaviour - they're not intended to be a data storage mechanism.
Pseudo-code example:
switch(httpResponseCode)
{
case 200:
parseResult(json);
break;
case 403:
parseForbidden(json);
break;
case 500:
parseServerError(json);
break;
default:
// bad response code, handle appropriately
}