You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yaws反向代理场景下HTTP 500内部服务器错误的调试问询

Yaws反向代理返回500内部服务器错误

我使用Yaws作为反向代理,Yaws部署在名为yaws的Docker容器中,调用名为wsm的NGINX容器。极少情况下,从Opera或Edge调用时会出现500 Internal Server Error,但在Yaws内部调用则无此问题。

该如何调试此问题?

使用curl测试

在Linux中使用curl调用时也会出现同样情况:经过一段时间后返回HTTP/1.1 500 Internal Server Error,显然Yaws在此过程中参与了处理。

CS-1 16:33:06 :/tmp# curl -v 'https://wp:my_secret@sm.myurl.biz/equivoxx/2181/sitemap/?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1'
*   Trying 123.456.240.136:443...
* TCP_NODELAY set
* Connected to sm.myurl.biz (123.456.240.136) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
[...]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=myurl.biz
*  start date: Jun 15 00:00:00 2023 GMT
*  expire date: Sep 13 23:59:59 2023 GMT
*  subjectAltName: host "sm.myurl.biz" matched cert's "sm.myurl.biz"
*  issuer: C=AT; O=ZeroSSL; CN=ZeroSSL RSA Domain Secure Site CA
*  SSL certificate verify ok.
* Server auth using Basic with user 'wp'
> GET /equivoxx/2181/sitemap/?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1 HTTP/1.1
> Host: sm.myurl.biz
> Authorization: Basic d3A6Y2hlZiw0OA==
> User-Agent: curl/7.68.0
> Accept: */*
>
[working]
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 Internal Server Error
< Connection: close
< Server: Yaws 2.0.9
< Date: Wed, 26 Jul 2023 14:34:28 GMT
< Content-Length: 47
< Content-Type: text/html
< Vary: Accept-Encoding
<
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, close notify (256):
<html><h1>500 Internal Server Error</h1></html>

消息Mark bundle as not supporting multiuse并非错误信息,而是无影响的调试跟踪消息,与HTTP/1.1 500 Internal Server Error无关。

无参数时运行正常

例如,URL:

https://sm.myurl.biz/equimyurl/2181/sitemap/ 

在所有环境下运行正常,但带多个参数的URL:

https://sm.myurl.biz/equimyurl/2181/sitemap/?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1 

在浏览器或curl中返回错误。这些参数会触发数据库处理。

检查参数后发现,仅del_sm会触发500错误,该参数会通过删除部分表来清理旧数据,之后重建表以完成更新。此操作与Yaws无关,按理说无需担心,对吗?

绕过Yaws:在NGINX容器wsm中运行正常

在Yaws容器yaws内部,直接调用NGINX容器wsm的带参URL(即curl调用wsm)时运行正常:

curl -v 'wsm/equivoxx/2181/sitemap?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1' | tee /tmp/2181-wsm1.htm

curl输出符合预期,用浏览器打开输出文件2181-wsm1.htm也无问题,数据更新正常。Yaws不应修改输出,但仍返回500错误。

我设置了日志文件/tmp/tmp_'.$this->id_ex.'__hunt_500.log记录消息,以追踪源代码中可能出错的位置。不出所料,最后一条日志如下:

2023-07-24 19:56:26 elapsed_secs :14: -- L: 1189 M: Imp::_die HUNT_500  this->lg :en: this->id_ex :2181:

该消息表明,它是在Imp模块的_die方法中触发的,这是程序退出前执行的最后操作:

function _die() {
        $this->dba->close();
        $this->output->_display();
wp_str_to_file('L: '.__LINE__.' M: '.__METHOD__." this->lg :$this->lg: this->id_ex :$this->id_ex:", 
     '/tmp/tmp_'.$this->id_ex.'__hunt_500.log');
        exit;
    } # _die

由此可见,程序已完成预期操作,更新的数据也能证明这一点。那为何Yaws仍对输出有问题?

分析Yaws配置

我在Yaws中大量使用mnesia进行数据缓存,当然部分URL不应被缓存。参数rrr=1会禁用缓存,因此问题URL的输出不会被Yaws处理,而是直接透传。

相关的appmod函数如下:

out(A) ->
    S = check_intrusion(A#arg.querydata), % out(A) -> 
    if 
        S ->
            out_hello();                            % avoid bad Urls
        true ->                                      
             out_check_log(A)                       % out, real content, filter for cache
    end.

check_intrusion(U) ->                % avoid bad Urls
    case U of
        undefined ->
            0;
        _ ->
            L = ["/solr", "HelloThink", "XDEBUG_SESSION", "phpstorm", "invokefunction", "/TP/public"],
            [string:find(U, N) || N <- L, string:find(U, N) /= nomatch] /= []  
    end.

out_check_log(A) ->                                               
    R = check_log(A),                                          
    if 
        R ->                                           % logging turned on, pass through
            Url = get_uri(A) ++ get_cookies_string(A),         
            pass_through(Url);                               
        true ->                                              
            out_myurl(A)                                % real work, eventually saved by mnesia 
    end.
check_log(A) ->
    Url = get_uri_string(A),     
    R = string:str(Url, "log=1") > 0,
    R.

out_myurl(A) ->
    Del = yaws_api:queryvar(A, "del"),
    Lg = get_lang(A),                     % Lg is important
    Request_path = get_request_path(A),
    Lg_url = prepend_lg(Lg, Request_path),
    if 
        Del /= undefined ->
                url:delete_record(myurl, Lg),       
                % we have at least lg=...  will delete if we have no GET param
                url:delete_record(myurl, Lg_url);   
                % we have at least lg=...  delete the record
        true ->                                    % out_myurl Del
            ok
    end,
    case skip_this_url(Request_path) of                     
        false ->                          % we want to save or retrieve this
            Start = deb:get_timestamp(),  % timestamp -- a bit late, we miss some cycles
            url:init(),                                            % mnesia
            Html = select_or_record(A, Request_path, Start, Lg),   
            % should replace elapsed time with real data
            Html;     % we get cached data with fresh timestamp
        true ->                                                    
            pass_through(Request_path)    % out_myurl skip_this_url
    end.
skip_this_url(Url) ->                              % determine URLs not to be cached
    R = string:str(Url, ".ico") > 0
    orelse 
    string:str(Url, "search=") > 0                 % search is so fast and should be fresh
% [...]                                            % lots of conditions
    orelse 
    string:str(Url, "rrr=1") > 0                   % param to not cache
    orelse 
    string:str(Url, ".well-known") > 0             % letsencrypt
    orelse 
    string:str(Url, "uploads/") > 0
    orelse 
    string:str(Url, "xframe/") > 0,
    R.

pass_through(Request_path)  ->           
    Url = check_scheme(Request_path),
    inets:start(),
    ssl:start(),
    Url_2 = string:replace(Url, " ", "%2B"),
    Http_result = httpc:request(Url_2),
    {ok, {{V, S, R}, _, _}} = Http_result,               % we get the results
    case Http_result of
        {ok, {{_Version, 200, _ReasonPhrase}, _Headers, Html}} ->               
                    {html, Html};   % we get the results, display them
        {ok, {{_Version, 401, _ReasonPhrase}, _Headers, Html}} ->              
                    {html, Html};   % auth mechanism
        _ -> 
            {ok, {{_, Status, ReasonPhrase}, _, _}} = Http_result,
            not_200                 % some kind of error
    end.           

据我观察,rrr=1会调用pass_through函数,该函数仅执行httpc:request,并在收到200响应时透传结果。

总结与疑问

目前,该错误仅影响更新机制的输出,但未影响数据本身,数据更新正常。虽然输出无关紧要,但仍想解决此问题。

该如何调试?我未在yaws.conf中使用容易引发500错误的interception module。有哪些方法可用于排查此500错误?

wsm运行正常,不清楚为何会导致Yaws返回错误。

我已在Yaws容器中执行yaws --stop停止服务,然后通过以下命令重启以开启调试:

rm -f /tmp/myurl*.log
yaws -i 

但未发现任何差异。

查看myurl.log时,发现大量如下类型的日志:

------2023-07-26 12:57:57---
139
 url:delete_record RECORD NOT FOUND, mnesia:abort(not_exist)
 Key "de"

这只是函数delete_record(Table, Key)的通知消息,其中Key会遍历涉及的5种语言。

查看Yaws的跟踪日志,未发现差异:两种情况的日志均以以下内容结尾:

Mod:myurl line:575 'select_or_record(A)=SELECT_OR_RECORD==SELECT_NOT_FOUND=> INSERT_WEB_PAGE Cookie' ["<!-- : -->"]

请问还有哪些排查方向?

内容的提问来源于stack exchange,提问作者kklepper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 18:47:00