Jump to content
Sign in to follow this  

Return resource location but don't follow with same method

Recommended Posts

I have a class ServerBridge which is used to proxy browser ajax requests received by a web server to another API server.  To update a record (or create a record is similar):

  1. Browser client makes PUT request to web server.
  2. Web server modifies the uri path and passes the body plus headers connection, accept, accept-encoding, accept-language, content-type, content-length only to the API server.
  3. The API server does work and returns the location of the resource on itself to the web server.
  4. The web server modifies the location header to point to the resource location on itself, and returns all received headers except Date, Server, X-Powered-By, Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers to the browser client.
  5. Browser client makes another undesired PUT request to web server.

Why is step 5 occurring and how do I prevent it?  I don't really mind the browser client making another request to the resource, but it would need to be GET and not PUT.  Also, is my white-list headers to forward to api server and black-list headers to return to browser client appropriate?

I haven't posted the ServerBridge script (but can if needed), but did include the requests and headers related to the browser client, web server, and api server below.

Browser client JavaScript


    simpleEdit: function(url, options) {
        var dOptions={type: 'text', ajaxOptions: {type: "PUT"}, placement: 'right', send: 'always' };
        return this.each(function() {
            var $t=$(this);
            var o=Object.assign({
                url: url+'/'+$t.closest('tr').data('id'),
                title: 'Enter '+$t.data('name')
            }, dOptions, options);

Web-server index.php

$c['serverBridge'] = function ($c) {
    return new \Greenbean\ServerBridge\ServerBridge(
        new \GuzzleHttp\Client([
            'base_uri' => $c['settings']['server']['scheme'].'://'.$c['settings']['server']['host'],
            'headers' => ['X-Secret-Key' => $c['settings']['server']['key']],
            'timeout'  => 30,
            'allow_redirects' => false,
        new \Greenbean\ServerBridge\SlimHttpClientHandler()

$app->put('/api/accounts/{id:[0-9]+}', function (Request $request, Response $response) {
    return $this->serverBridge->proxy($request, $response,
        function(string $path):string{
            return substr($path, 4);       //Remove "/api" from uri
        function(array $headers):array{
            if(!empty($headers['Location'])) {
                $headers['Location'] = ['/api'.$headers['Location'][0]];  //Add "/api" to redirect header if it exists
            return $headers;
$app->POST('/api/accounts/{id:[0-9]+}', function (Request $request, Response $response) {/*Similar to PUT*/});



class ServerBridge
    public function __construct(\GuzzleHttp\Client $httpClient, ?HttpClientHandlerInterface $httpClientHandler=null){
        $this->httpClientHandler=$httpClientHandler;    //Whether to use Slim or Sympony HTTP requests and responses

    public function proxy($clientRequest, $clientResponse=null, \Closure $modifyPath=null, \Closure $modifyHeaders=null) {
        //Accept a Slim or Sympony HTTP request, forward it to API server via cURL, and return the cURL response
        //$modifyPath will modify REQUEST_URI before sending to API server 
        //$modifyHeaders will modify response headers before sending to calling client 
        return $response;

API Server index.php

$app->put('/accounts/{id:[0-9]+}', function (Request $request, Response $response, $args) {
        $this->accounts->update($args['id'], $request->getParsedBody());
        return $response->withRedirect('/accounts/'.$args['id'], 302);

$app->post('/accounts', function (Request $request, Response $response) {
        $account = $this->accounts->create($request->getParsedBody());
        return $response->withRedirect('/accounts/'.$account['id'], 302);

Browser Client
    Request URL:https://admin.tapmeister.com/api/accounts/1974750116
    Request method:PUT
    Remote address:
    Status code:302
    Request headers (477 B)    
        Raw headers
        Accept    */*
        Accept-Encoding    gzip, deflate, br
        Accept-Language    en-US,en;q=0.5
        Connection    keep-alive
        Content-Length    30
        Content-Type    application/x-www-form-urlencoded; charset=UTF-8
        Cookie    PHPSESSID=d11m0k37mf3veaeg0rs350rvkp
        Host    admin.tapmeister.com
        Referer   https://admin.tapmeister.com/
        User-Agent    Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/68.0
        X-Requested-With    XMLHttpRequest
    Response headers (408 B)    
        Cache-Control no-store, no-cache, must-revalidate
        Connection Keep-Alive, Keep-Alive
        Content-Length 0
        Content-Type text/html; charset=UTF-8
        Date Sat, 31 Aug 2019 16:18:47 GMT
        Expires    Thu, 19 Nov 1981 08:52:00 GMT
        Keep-Alive    timeout=5, max=100
        Location    /api/accounts/1974750116
        Pragma    no-cache
        Server    Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips
        X-Powered-By    PHP/7.3.9

Web Server
    PUT admin.tapmeister.com/api/accounts/1974750116  REDIRECT_STATUS: 200 REDIRECT_URL: /api/accounts/1974750116
    Request headers
        Cookie: PHPSESSID=d11m0k37mf3veaeg0rs350rvkp
        Connection: keep-alive
        Content-Length: 30
        X-Requested-With: XMLHttpRequest
        Content-Type: application/x-www-form-urlencoded; charset=UTF-8
        Referer: https://admin.tapmeister.com/
        Accept-Encoding: gzip, deflate, br
        Accept-Language: en-US,en;q=0.5
        Accept: */*
        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
        Host: admin.tapmeister.com
    Response headers    
        Date: [Sat, 31 Aug 2019 16:18:47 GMT],
        Server: [Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips],
        X-Powered-By: [PHP/7.3.9],
        Location: [/accounts/1974750116],
        Content-Length: [0],
        Access-Control-Allow-Origin: [*],
        Access-Control-Allow-Methods: [GET, POST, PUT, DELETE, OPTIONS],
        Access-Control-Allow-Headers: [X-Secret-Key, Origin, X-Requested-With, Content-Type, Accept],
        Keep-Alive: [timeout=5, max=100],
        Connection: [Keep-Alive],
        Content-Type: [text/html; charset=UTF-8]

API Server
    PUT api.admin.tapmeister.com/accounts/1974750116 REDIRECT_STATUS: 200 REDIRECT_URL: /accounts/1974750116
    Request headers
        Content-Length: 30
        Accept: */*
        Accept-Language: en-US,en;q=0.5
        Accept-Encoding: gzip, deflate, br
        Content-Type: application/x-www-form-urlencoded; charset=UTF-8
        Connection: keep-alive
        Host: api.admin.tapmeister.com
        User-Agent: GuzzleHttp/6.3.3 curl/7.29.0 PHP/7.3.9
        X-Secret-Key: secretKey

Edited by NotionCommotion

Share this post

Link to post
Share on other sites

Your webserver is responding the the browser with a redirect (Status 302) so the browser is trying to follow that redirect.

If you're creating a new resource you should be responding with status code 201.

If you're updating an existing resource you should respond with status code 200.


  • Like 1

Share this post

Link to post
Share on other sites

Slim will use 302 by default with withRedirect($url) which is fine, but also returns 302 using withRedirect($url, 200).  201 for create and 204 for update, however, work as desired.  Thanks

Share this post

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.