Rails responding with 400 Bad Request in production but not in development (application/json body)

I have two rails apps that are sending data to each other via HTTP requests.

My first app sends its client data to the other app via this code:

    def sync_client_data
        Rails.logger.debug("*** Sending client data to tech app.")

        uri = URI(endpoint("clients")) # generates a URL e.g. my.api.com/clients

        Rails.logger.debug("*** Sending to URI: #{uri}")

        req = Net::HTTP::Post.new(uri, {
            'Content-Type' => 'application/json',
            'Authorization' => Rails.application.config.tech_app_api_key
        })

        req.body = Client.all.to_json(include: :locations)

        res = Net::HTTP.start(uri.hostname, uri.port) do |http|
            http.request(req)
        end

        Rails.logger.debug("*** Response: #{res}") # in production the response is: #<Net::HTTPBadRequest:0x000055b70cd18788>

        return redirect_to service_orders_path
    end

In development, there is no error. The other app receives the JSON data just fine, and is able to process it, do things with it, etc.

In production however, the app on the receiving end just responds with 400 bad request.

The receiving end looks like this:

class Api::ClientsController < ApiController
    def create
        Rails.logger.debug("Received request to sync client data.")

        params["_json"].each do |client_json|
            # do stuff...
        end
    end
end

I don’t even see the debug output in the logs that should say “Received request to sync client data.”

A bit stumped as to how I’m going to debug this.

I found this URL with a possible solution: Catching Invalid JSON Parse Errors with Rack Middleware

I tried putting this code in config/initializers/catch_json_parse_errors_middleware.rb:

class CatchJsonParseErrors
    def initialize(app)
        @app = app
    end
  
    def call(env)
        begin
            Rails.logger.debug("*** And here we are... @app.call(env)") # this gets called
            @app.call(env)
        rescue ActionDispatch::ParamsParser::ParseError => error
            Rails.logger.debug("*** And here we are... ParseError") # this never gets called
            if env['CONTENT_TYPE'] =~ /application\/json/
                error_output = "There was a problem in the JSON you submitted: #{error.class}"
                return [
                    400, { "Content-Type" => "application/json" },
                    [ { status: 400, error: error_output }.to_json ]
                ]
            else
                raise error
            end
        end
    end
end

Rails.application.config.middleware.insert_before Rack::Head, CatchJsonParseErrors

So it looks like this “bad request” error isn’t related to JSON.

I figured out what the problem was.

        res = Net::HTTP.start(uri.hostname, uri.port) do |http|
            http.request(req)
        end

        Rails.logger.debug("*** Response Body: #{res.read_body}")
D, [2021-04-15T16:26:35.590303 #3111646] DEBUG -- : [b8a9545c-08c7-41df-b70e-708152eaad0e] *** Response Body: <html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>

All because I didn’t set use_ssl flag on my Net::HTTP.start() call.