I had such problem as well previously, I believe that setting NetworkOwnership won’t solve the problem as a whole, but I heard that making a custom Vehicle seat and using UserInputService would do most of the fixing.
To dive a little deeper into the problem we are experiencing here: A player wants to control a car. However the physics of the car can be calculated by different parties. Usually this party is the server, but it can also be nearby players (clients). If I push a block, the party responsible for the physics of the block (the network owner) has to calculate where the block ends up in the next frame. If this party is the server or another client, a signal from my computer has to be sent to that party to calculate the physics, and then sent back to me. That takes a lot of time!
The solution is the SetNetworkOwner method as mentioned before. This method has to be called from the server. What it does is it forces the physics calculations to be done by the mentioned party (nil for server, a player for a player). So if a player wants to control a car, the server has to force the network owner to be that player.
To make the car work, you will now need a LocalScript. Since the server has given you the network ownership / physics ownership, you can now control the car without any lag, because you are the owner!
Usually you should put a LocalScript somewhere in the PlayerScripts or PlayerGui container. Given that a car is not an interface, the most logical place would be in the PlayerScripts. Figuring out which car needs to be controlled is something that totally depends on your implementation. You could tag every car using CollectionService and then figure out what the nearest car is, or you could use a Touched signal to determine whenever the player touches a car. This is a problem you should probably tackle and figure out on your own.